Bump version numbers for 3.13
[maemo-rb.git] / firmware / common / file.c
blobcfebd0622f8aa934189bb3b8bf5bd733ae2ca1bb
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 # ifdef HAVE_MULTIVOLUME
112 int volume = strip_volume(pathname, pathnamecopy);
113 # endif
115 int ce = dircache_get_entry_id(pathname);
116 if (ce < 0)
118 errno = ENOENT;
119 file->busy = false;
120 return -7;
123 long startcluster = _dircache_get_entry_startcluster(ce);
124 fat_open(IF_MV2(volume,)
125 startcluster,
126 &(file->fatfile),
127 NULL);
128 struct dirinfo *info = _dircache_get_entry_dirinfo(ce);
129 file->size = info->size;
130 file->attr = info->attribute;
131 file->cacheoffset = -1;
132 file->fileoffset = 0;
134 return fd;
136 #endif
138 strlcpy(pathnamecopy, pathname, pathnamesize);
140 /* locate filename */
141 name=strrchr(pathnamecopy+1,'/');
142 if ( name ) {
143 *name = 0;
144 dir = opendir_uncached(pathnamecopy);
145 *name = '/';
146 name++;
148 else {
149 dir = opendir_uncached("/");
150 name = pathnamecopy+1;
152 if (!dir) {
153 DEBUGF("Failed opening dir\n");
154 errno = EIO;
155 file->busy = false;
156 return -4;
159 if(name[0] == 0) {
160 DEBUGF("Empty file name\n");
161 errno = EINVAL;
162 file->busy = false;
163 closedir_uncached(dir);
164 return -5;
167 /* scan dir for name */
168 while ((entry = readdir_uncached(dir))) {
169 if ( !strcasecmp(name, entry->d_name) ) {
170 fat_open(IF_MV2(dir->fatdir.file.volume,)
171 entry->startcluster,
172 &(file->fatfile),
173 &(dir->fatdir));
174 file->size = file->trunc ? 0 : entry->info.size;
175 file->attr = entry->info.attribute;
176 break;
180 if ( !entry ) {
181 LDEBUGF("Didn't find file %s\n",name);
182 if ( file->write && (flags & O_CREAT) ) {
183 rc = fat_create_file(name,
184 &(file->fatfile),
185 &(dir->fatdir));
186 if (rc < 0) {
187 DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy);
188 errno = EIO;
189 file->busy = false;
190 closedir_uncached(dir);
191 return rc * 10 - 6;
193 #ifdef HAVE_DIRCACHE
194 dircache_add_file(pathname, file->fatfile.firstcluster);
195 #endif
196 file->size = 0;
197 file->attr = 0;
199 else {
200 DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
201 errno = ENOENT;
202 file->busy = false;
203 closedir_uncached(dir);
204 return -7;
206 } else {
207 if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
208 errno = EISDIR;
209 file->busy = false;
210 closedir_uncached(dir);
211 return -8;
214 closedir_uncached(dir);
216 file->cacheoffset = -1;
217 file->fileoffset = 0;
219 if (file->write && (flags & O_APPEND)) {
220 rc = lseek(fd,0,SEEK_END);
221 if (rc < 0 )
222 return rc * 10 - 9;
225 #ifdef HAVE_DIRCACHE
226 if (file->write)
227 dircache_bind(fd, pathname);
228 #endif
230 return fd;
233 int file_open(const char* pathname, int flags)
235 /* By default, use the dircache if available. */
236 return open_internal(pathname, flags, true);
239 int close(int fd)
241 struct filedesc* file = &openfiles[fd];
242 int rc = 0;
244 LDEBUGF("close(%d)\n", fd);
246 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
247 errno = EINVAL;
248 return -1;
250 if (!file->busy) {
251 errno = EBADF;
252 return -2;
254 if (file->write) {
255 rc = fsync(fd);
256 if (rc < 0)
257 return rc * 10 - 3;
258 #ifdef HAVE_DIRCACHE
259 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
260 dircache_update_filetime(fd);
261 #endif
264 file->busy = false;
265 return 0;
268 int fsync(int fd)
270 struct filedesc* file = &openfiles[fd];
271 int rc = 0;
273 LDEBUGF("fsync(%d)\n", fd);
275 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
276 errno = EINVAL;
277 return -1;
279 if (!file->busy) {
280 errno = EBADF;
281 return -2;
283 if (file->write) {
284 /* flush sector cache */
285 if ( file->dirty ) {
286 rc = flush_cache(fd);
287 if (rc < 0)
289 /* when failing, try to close the file anyway */
290 fat_closewrite(&(file->fatfile), file->size, file->attr);
291 return rc * 10 - 3;
295 /* truncate? */
296 if (file->trunc) {
297 rc = ftruncate(fd, file->size);
298 if (rc < 0)
300 /* when failing, try to close the file anyway */
301 fat_closewrite(&(file->fatfile), file->size, file->attr);
302 return rc * 10 - 4;
306 /* tie up all loose ends */
307 rc = fat_closewrite(&(file->fatfile), file->size, file->attr);
308 if (rc < 0)
309 return rc * 10 - 5;
311 return 0;
314 int remove(const char* name)
316 int rc;
317 struct filedesc* file;
318 /* Can't use dircache now, because we need to access the fat structures. */
319 int fd = open_internal(name, O_WRONLY, false);
320 if ( fd < 0 )
321 return fd * 10 - 1;
323 file = &openfiles[fd];
324 #ifdef HAVE_DIRCACHE
325 dircache_remove(name);
326 #endif
327 rc = fat_remove(&(file->fatfile));
328 if ( rc < 0 ) {
329 DEBUGF("Failed removing file: %d\n", rc);
330 errno = EIO;
331 return rc * 10 - 3;
334 file->size = 0;
336 rc = close(fd);
337 if (rc<0)
338 return rc * 10 - 4;
340 return 0;
343 int rename(const char* path, const char* newpath)
345 int rc, fd;
346 DIR_UNCACHED* dir;
347 char* nameptr;
348 char* dirptr;
349 struct filedesc* file;
350 char newpath2[MAX_PATH];
352 /* verify new path does not already exist */
353 /* If it is a directory, errno == EISDIR if the name exists */
354 fd = open(newpath, O_RDONLY);
355 if ( fd >= 0 || errno == EISDIR) {
356 close(fd);
357 errno = EBUSY;
358 return -1;
360 close(fd);
362 fd = open_internal(path, O_RDONLY, false);
363 if ( fd < 0 ) {
364 errno = EIO;
365 return fd * 10 - 2;
368 /* extract new file name */
369 nameptr = strrchr(newpath,'/');
370 if (nameptr)
371 nameptr++;
372 else {
373 close(fd);
374 return - 3;
377 /* Extract new path */
378 strcpy(newpath2, newpath);
380 dirptr = strrchr(newpath2,'/');
381 if(dirptr)
382 *dirptr = 0;
383 else {
384 close(fd);
385 return - 4;
388 dirptr = newpath2;
390 if(strlen(dirptr) == 0) {
391 dirptr = "/";
394 dir = opendir_uncached(dirptr);
395 if(!dir) {
396 close(fd);
397 return - 5;
400 file = &openfiles[fd];
402 rc = fat_rename(&file->fatfile, &dir->fatdir, nameptr,
403 file->size, file->attr);
404 #ifdef HAVE_MULTIVOLUME
405 if ( rc == -1) {
406 close(fd);
407 closedir_uncached(dir);
408 DEBUGF("Failed renaming file across volumnes: %d\n", rc);
409 errno = EXDEV;
410 return -6;
412 #endif
413 if ( rc < 0 ) {
414 close(fd);
415 closedir_uncached(dir);
416 DEBUGF("Failed renaming file: %d\n", rc);
417 errno = EIO;
418 return rc * 10 - 7;
421 #ifdef HAVE_DIRCACHE
422 dircache_rename(path, newpath);
423 #endif
425 rc = close(fd);
426 if (rc<0) {
427 closedir_uncached(dir);
428 errno = EIO;
429 return rc * 10 - 8;
432 rc = closedir_uncached(dir);
433 if (rc<0) {
434 errno = EIO;
435 return rc * 10 - 9;
438 return 0;
441 int ftruncate(int fd, off_t size)
443 int rc, sector;
444 struct filedesc* file = &openfiles[fd];
446 sector = size / SECTOR_SIZE;
447 if (size % SECTOR_SIZE)
448 sector++;
450 rc = fat_seek(&(file->fatfile), sector);
451 if (rc < 0) {
452 errno = EIO;
453 return rc * 10 - 1;
456 rc = fat_truncate(&(file->fatfile));
457 if (rc < 0) {
458 errno = EIO;
459 return rc * 10 - 2;
462 file->size = size;
463 #ifdef HAVE_DIRCACHE
464 dircache_update_filesize(fd, size, file->fatfile.firstcluster);
465 #endif
467 return 0;
470 static int flush_cache(int fd)
472 int rc;
473 struct filedesc* file = &openfiles[fd];
474 long sector = file->fileoffset / SECTOR_SIZE;
476 DEBUGF("Flushing dirty sector cache\n");
478 /* make sure we are on correct sector */
479 rc = fat_seek(&(file->fatfile), sector);
480 if ( rc < 0 )
481 return rc * 10 - 3;
483 rc = fat_readwrite(&(file->fatfile), 1, file->cache, true );
485 if ( rc < 0 ) {
486 if(file->fatfile.eof)
487 errno = ENOSPC;
489 return rc * 10 - 2;
492 file->dirty = false;
494 return 0;
497 static int readwrite(int fd, void* buf, long count, bool write)
499 long sectors;
500 long nread=0;
501 struct filedesc* file;
502 int rc;
503 #ifdef STORAGE_NEEDS_ALIGN
504 long i;
505 int rc2;
506 #endif
508 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
509 errno = EINVAL;
510 return -1;
513 file = &openfiles[fd];
515 if ( !file->busy ) {
516 errno = EBADF;
517 return -1;
520 if(file->attr & FAT_ATTR_DIRECTORY) {
521 errno = EISDIR;
522 return -1;
525 LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
526 fd,(long)buf,count,write?"write":"read");
528 /* attempt to read past EOF? */
529 if (!write && count > file->size - file->fileoffset)
530 count = file->size - file->fileoffset;
532 /* any head bytes? */
533 if ( file->cacheoffset != -1 ) {
534 int offs = file->cacheoffset;
535 int headbytes = MIN(count, SECTOR_SIZE - offs);
537 if (write) {
538 memcpy( file->cache + offs, buf, headbytes );
539 file->dirty = true;
541 else {
542 memcpy( buf, file->cache + offs, headbytes );
545 if (offs + headbytes == SECTOR_SIZE) {
546 if (file->dirty) {
547 rc = flush_cache(fd);
548 if ( rc < 0 ) {
549 errno = EIO;
550 return rc * 10 - 2;
553 file->cacheoffset = -1;
555 else {
556 file->cacheoffset += headbytes;
559 nread = headbytes;
560 count -= headbytes;
563 /* If the buffer has been modified, either it has been flushed already
564 * (if (offs+headbytes == SECTOR_SIZE)...) or does not need to be (no
565 * more data to follow in this call). Do NOT flush here. */
567 /* read/write whole sectors right into/from the supplied buffer */
568 sectors = count / SECTOR_SIZE;
569 rc = 0;
570 if ( sectors ) {
571 #ifdef STORAGE_NEEDS_ALIGN
572 if (((uint32_t)buf + nread) & (CACHEALIGN_SIZE - 1))
573 for (i = 0; i < sectors; i++)
575 if (write) memcpy(file->cache, buf+nread+i*SECTOR_SIZE, SECTOR_SIZE);
576 rc2 = fat_readwrite(&(file->fatfile), 1, file->cache, write );
577 if (rc2 < 0)
579 rc = rc2;
580 break;
582 else rc += rc2;
583 if (!write) memcpy(buf+nread+i*SECTOR_SIZE, file->cache, SECTOR_SIZE);
585 else
586 #endif
587 rc = fat_readwrite(&(file->fatfile), sectors, (unsigned char*)buf+nread, write );
588 if ( rc < 0 ) {
589 DEBUGF("Failed read/writing %ld sectors\n",sectors);
590 errno = EIO;
591 if(write && file->fatfile.eof) {
592 DEBUGF("No space left on device\n");
593 errno = ENOSPC;
594 } else {
595 file->fileoffset += nread;
597 file->cacheoffset = -1;
598 /* adjust file size to length written */
599 if ( write && file->fileoffset > file->size )
601 file->size = file->fileoffset;
602 #ifdef HAVE_DIRCACHE
603 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
604 #endif
606 return nread ? nread : rc * 10 - 4;
608 else {
609 if ( rc > 0 ) {
610 nread += rc * SECTOR_SIZE;
611 count -= sectors * SECTOR_SIZE;
613 /* if eof, skip tail bytes */
614 if ( rc < sectors )
615 count = 0;
617 else {
618 /* eof */
619 count=0;
622 file->cacheoffset = -1;
626 /* any tail bytes? */
627 if ( count ) {
628 if (write) {
629 if ( file->fileoffset + nread < file->size ) {
630 /* sector is only partially filled. copy-back from disk */
631 LDEBUGF("Copy-back tail cache\n");
632 rc = fat_readwrite(&(file->fatfile), 1, file->cache, false );
633 if ( rc < 0 ) {
634 DEBUGF("Failed writing\n");
635 errno = EIO;
636 file->fileoffset += nread;
637 file->cacheoffset = -1;
638 /* adjust file size to length written */
639 if ( file->fileoffset > file->size )
641 file->size = file->fileoffset;
642 #ifdef HAVE_DIRCACHE
643 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
644 #endif
646 return nread ? nread : rc * 10 - 5;
648 /* seek back one sector to put file position right */
649 rc = fat_seek(&(file->fatfile),
650 (file->fileoffset + nread) /
651 SECTOR_SIZE);
652 if ( rc < 0 ) {
653 DEBUGF("fat_seek() failed\n");
654 errno = EIO;
655 file->fileoffset += nread;
656 file->cacheoffset = -1;
657 /* adjust file size to length written */
658 if ( file->fileoffset > file->size )
660 file->size = file->fileoffset;
661 #ifdef HAVE_DIRCACHE
662 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
663 #endif
665 return nread ? nread : rc * 10 - 6;
668 memcpy( file->cache, (unsigned char*)buf + nread, count );
669 file->dirty = true;
671 else {
672 rc = fat_readwrite(&(file->fatfile), 1, file->cache,false);
673 if (rc < 1 ) {
674 DEBUGF("Failed caching sector\n");
675 errno = EIO;
676 file->fileoffset += nread;
677 file->cacheoffset = -1;
678 return nread ? nread : rc * 10 - 7;
680 memcpy( (unsigned char*)buf + nread, file->cache, count );
683 nread += count;
684 file->cacheoffset = count;
687 file->fileoffset += nread;
688 LDEBUGF("fileoffset: %ld\n", file->fileoffset);
690 /* adjust file size to length written */
691 if ( write && file->fileoffset > file->size )
693 file->size = file->fileoffset;
694 #ifdef HAVE_DIRCACHE
695 dircache_update_filesize(fd, file->size, file->fatfile.firstcluster);
696 #endif
699 return nread;
702 ssize_t write(int fd, const void* buf, size_t count)
704 if (!openfiles[fd].write) {
705 errno = EACCES;
706 return -1;
708 return readwrite(fd, (void *)buf, count, true);
711 ssize_t read(int fd, void* buf, size_t count)
713 return readwrite(fd, buf, count, false);
717 off_t lseek(int fd, off_t offset, int whence)
719 off_t pos;
720 long newsector;
721 long oldsector;
722 int sectoroffset;
723 int rc;
724 struct filedesc* file = &openfiles[fd];
726 LDEBUGF("lseek(%d,%ld,%d)\n",fd,offset,whence);
728 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
729 errno = EINVAL;
730 return -1;
732 if ( !file->busy ) {
733 errno = EBADF;
734 return -1;
737 switch ( whence ) {
738 case SEEK_SET:
739 pos = offset;
740 break;
742 case SEEK_CUR:
743 pos = file->fileoffset + offset;
744 break;
746 case SEEK_END:
747 pos = file->size + offset;
748 break;
750 default:
751 errno = EINVAL;
752 return -2;
754 if ((pos < 0) || (pos > file->size)) {
755 errno = EINVAL;
756 return -3;
759 /* new sector? */
760 newsector = pos / SECTOR_SIZE;
761 oldsector = file->fileoffset / SECTOR_SIZE;
762 sectoroffset = pos % SECTOR_SIZE;
764 if ( (newsector != oldsector) ||
765 ((file->cacheoffset==-1) && sectoroffset) ) {
767 if ( newsector != oldsector ) {
768 if (file->dirty) {
769 rc = flush_cache(fd);
770 if (rc < 0)
771 return rc * 10 - 5;
774 rc = fat_seek(&(file->fatfile), newsector);
775 if ( rc < 0 ) {
776 errno = EIO;
777 return rc * 10 - 4;
780 if ( sectoroffset ) {
781 rc = fat_readwrite(&(file->fatfile), 1, file->cache ,false);
782 if ( rc < 0 ) {
783 errno = EIO;
784 return rc * 10 - 6;
786 file->cacheoffset = sectoroffset;
788 else
789 file->cacheoffset = -1;
791 else
792 if ( file->cacheoffset != -1 )
793 file->cacheoffset = sectoroffset;
795 file->fileoffset = pos;
797 return pos;
800 off_t filesize(int fd)
802 struct filedesc* file = &openfiles[fd];
804 if (fd < 0 || fd > MAX_OPEN_FILES-1) {
805 errno = EINVAL;
806 return -1;
808 if ( !file->busy ) {
809 errno = EBADF;
810 return -1;
813 return file->size;
817 /* release all file handles on a given volume "by force", to avoid leaks */
818 int release_files(int volume)
820 struct filedesc* pfile = openfiles;
821 int fd;
822 int closed = 0;
823 for ( fd=0; fd<MAX_OPEN_FILES; fd++, pfile++)
825 #ifdef HAVE_MULTIVOLUME
826 if (pfile->fatfile.volume == volume)
827 #else
828 (void)volume;
829 #endif
831 pfile->busy = false; /* mark as available, no further action */
832 closed++;
835 return closed; /* return how many we did */