📄 fat_file.c
字号:
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
*
* Copyright (C) 2002 by Linus Nielsen Feltzing
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "fat_file.h"
#include "fat.h"
#define MAX_OPEN_FILES 10
#ifndef MIN
#define MIN(a, b) (((a)<(b))?(a):(b))
#endif
/*
These functions provide a roughly POSIX-compatible file IO API.
Since the fat32 driver only manages sectors, we maintain a one-sector
cache for each open file. This way we can provide byte access without
having to re-read the sector each time.
The penalty is the RAM used for the cache and slightly more complex code.
*/
#define BYTES2INT32(array,pos) \
((long)array[pos] | ((long)array[pos+1] << 8 ) | \
((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 ))
struct filedesc {
unsigned char cache[SECTOR_SIZE];
int cacheoffset; /* invariant: 0 <= cacheoffset <= SECTOR_SIZE */
long fileoffset;
long size;
int attr;
struct fat_file fatfile;
bool busy;
bool write;
bool dirty;
bool trunc;
};
static struct filedesc openfiles[MAX_OPEN_FILES];
static struct partinfo part[8]; /* space for 4 partitions on 2 drives */
static int vol_drive[NUM_VOLUMES]; /* mounted to which drive (-1 if none) */
#define MAX_OPEN_DIRS 8
static DIR opendirs[MAX_OPEN_DIRS];
int rb_fat_fstat( int fd, struct stat *st )
{
int ret_code;
struct filedesc* file = &openfiles[fd];
if ( !file->busy )
{
errno = EBADF;
return -1;
}
st->st_size = file->size;
// st->st_mode = fd->fd_flags;
return( 0 );
}
int rb_fat_open( const char* pathname, int flags )
{
DIR* dir;
struct dirent* entry;
int fd;
char pathnamecopy[MAX_PATH];
char* name;
struct filedesc* file = NULL;
int rc;
LDEBUGF("open(\"%s\",%d)\n",pathname,flags);
if ( pathname[0] != '/' ) {
DEBUGF("'%s' is not an absolute path.\n",pathname);
DEBUGF("Only absolute pathnames supported at the moment\n");
errno = EINVAL;
return -1;
}
/* find a free file descriptor */
for ( fd=0; fd<MAX_OPEN_FILES; fd++ )
if ( !openfiles[fd].busy )
break;
if ( fd == MAX_OPEN_FILES ) {
DEBUGF("Too many files open\n");
errno = EMFILE;
return -2;
}
file = &openfiles[fd];
memset(file, 0, sizeof(struct filedesc));
if (flags & (O_RDWR | O_WRONLY)) {
file->write = true;
if (flags & O_TRUNC)
file->trunc = true;
}
file->busy = true;
strncpy(pathnamecopy,pathname,sizeof(pathnamecopy));
pathnamecopy[sizeof(pathnamecopy)-1] = 0;
/* locate filename */
name=strrchr(pathnamecopy+1,'/');
if ( name ) {
*name = 0;
dir = rb_fat_opendir(pathnamecopy);
*name = '/';
name++;
}
else {
dir = rb_fat_opendir("/");
name = pathnamecopy+1;
}
if (!dir) {
DEBUGF("Failed opening dir\n");
errno = EIO;
file->busy = false;
return -4;
}
if(name[0] == 0) {
DEBUGF("Empty file name\n");
errno = EINVAL;
file->busy = false;
rb_fat_closedir(dir);
return -5;
}
/* scan dir for name */
while ((entry = readdir(dir))) {
if ( !strcasecmp(name, entry->d_name) ) {
fat_open(IF_MV2(dir->fatdir.file.volume,)
entry->startcluster,
&(file->fatfile),
&(dir->fatdir));
file->size = file->trunc ? 0 : entry->size;
file->attr = entry->attribute;
break;
}
}
if ( !entry ) {
LDEBUGF("Didn't find file %s\n",name);
if ( file->write && (flags & O_CREAT) ) {
rc = fat_create_file(name,
&(file->fatfile),
&(dir->fatdir));
if (rc < 0) {
DEBUGF("Couldn't create %s in %s\n",name,pathnamecopy);
errno = EIO;
file->busy = false;
rb_fat_closedir(dir);
return rc * 10 - 6;
}
#ifdef HAVE_DIRCACHE
dircache_add_file(pathname);
#endif
file->size = 0;
file->attr = 0;
}
else {
DEBUGF("Couldn't find %s in %s\n",name,pathnamecopy);
errno = ENOENT;
file->busy = false;
rb_fat_closedir(dir);
return -7;
}
} else {
if(file->write && (file->attr & FAT_ATTR_DIRECTORY)) {
errno = EISDIR;
file->busy = false;
rb_fat_closedir(dir);
return -8;
}
}
rb_fat_closedir(dir);
file->cacheoffset = -1;
file->fileoffset = 0;
if (file->write && (flags & O_APPEND)) {
rc = rb_fat_lseek(fd,0,SEEK_END);
if (rc < 0 )
return rc * 10 - 9;
}
#ifdef HAVE_DIRCACHE
if (file->write)
dircache_bind(fd, pathname);
#endif
return fd;
}
int rb_fat_close(int fd)
{
struct filedesc* file = &openfiles[fd];
int rc = 0;
LDEBUGF("close(%d)\n", fd);
if (fd < 0 || fd > MAX_OPEN_FILES-1) {
errno = EINVAL;
return -1;
}
if (!file->busy) {
errno = EBADF;
return -2;
}
if (file->write) {
rc = rb_fat_fsync(fd);
if (rc < 0)
return rc * 10 - 3;
}
file->busy = false;
return 0;
}
struct dirent* readdir(DIR* dir)
{
struct fat_direntry entry;
struct dirent* theent = &(dir->theent);
if (!dir->busy)
return NULL;
#ifdef HAVE_MULTIVOLUME
/* Volumes (secondary file systems) get inserted into the root directory
of the first volume, since we have no separate top level. */
if (dir->volumecounter >= 0 /* on a root dir */
&& dir->volumecounter < NUM_VOLUMES /* in range */
&& dir->fatdir.file.volume == 0) /* at volume 0 */
{ /* fake special directories, which don't really exist, but
will get redirected upon rb_fat_opendir() */
while (++dir->volumecounter < NUM_VOLUMES)
{
if (fat_ismounted(dir->volumecounter))
{
memset(theent, 0, sizeof(*theent));
theent->attribute = FAT_ATTR_DIRECTORY | FAT_ATTR_VOLUME;
snprintf(theent->d_name, sizeof(theent->d_name),
vol_names, dir->volumecounter);
return theent;
}
}
}
#endif
/* normal directory entry fetching follows here */
if (fat_getnext(&(dir->fatdir),&entry) < 0)
return NULL;
if ( !entry.name[0] )
return NULL;
strncpy(theent->d_name, entry.name, sizeof( theent->d_name ) );
theent->attribute = entry.attr;
theent->size = entry.filesize;
theent->startcluster = entry.firstcluster;
theent->wrtdate = entry.wrtdate;
theent->wrttime = entry.wrttime;
return theent;
}
DIR* rb_fat_opendir(const char* name)
{
char namecopy[MAX_PATH];
char* part;
char* end;
struct fat_direntry entry;
int dd;
DIR* pdir = opendirs;
#ifdef HAVE_MULTIVOLUME
int volume;
#endif
if ( name[0] != '/' ) {
DEBUGF("Only absolute paths supported right now\n");
return NULL;
}
/* find a free dir descriptor */
for ( dd=0; dd<MAX_OPEN_DIRS; dd++, pdir++)
if ( !pdir->busy )
break;
if ( dd == MAX_OPEN_DIRS ) {
DEBUGF("Too many dirs open\n");
errno = EMFILE;
return NULL;
}
pdir->busy = true;
#ifdef HAVE_MULTIVOLUME
/* try to extract a heading volume name, if present */
volume = strip_volume(name, namecopy);
pdir->volumecounter = 0;
#else
strncpy(namecopy,name,sizeof(namecopy)); /* just copy */
namecopy[sizeof(namecopy)-1] = '\0';
#endif
if ( fat_opendir(IF_MV2(volume,) &pdir->fatdir, 0, NULL) < 0 ) {
DEBUGF("Failed opening root dir\n");
pdir->busy = false;
return NULL;
}
for ( part = strtok_r(namecopy, "/", &end); part;
part = strtok_r(NULL, "/", &end)) {
/* scan dir for name */
while (1) {
if ((fat_getnext(&pdir->fatdir,&entry) < 0) ||
(!entry.name[0])) {
pdir->busy = false;
return NULL;
}
if ( (entry.attr & FAT_ATTR_DIRECTORY) &&
(!strcasecmp(part, entry.name)) ) {
pdir->parent_dir = pdir->fatdir;
if ( fat_opendir(IF_MV2(volume,)
&pdir->fatdir,
entry.firstcluster,
&pdir->parent_dir) < 0 ) {
DEBUGF("Failed opening dir '%s' (%ld)\n",
part, entry.firstcluster);
pdir->busy = false;
return NULL;
}
#ifdef HAVE_MULTIVOLUME
pdir->volumecounter = -1; /* n.a. to subdirs */
#endif
break;
}
}
}
return pdir;
}
int rb_fat_closedir(DIR* dir)
{
dir->busy=false;
return 0;
}
static int get_free_volume(void)
{
int i;
for (i=0; i<NUM_VOLUMES; i++)
{
if (vol_drive[i] == -1) /* unassigned? */
return i;
}
return -1; /* none found */
}
int disk_mount(int drive)
{
int i;
int mounted = 0; /* reset partition-on-drive flag */
int volume = get_free_volume();
struct partinfo* pinfo = disk_init(IF_MV(drive));
if (pinfo == NULL)
{
return 0;
}
for (i=0; volume != -1 && i<4; i++)
{
if (!fat_mount(IF_MV2(volume,) IF_MV2(drive,) pinfo[i].start))
{
mounted++;
vol_drive[volume] = drive; /* remember the drive for this volume */
volume = get_free_volume(); /* prepare next entry */
}
}
if (mounted == 0 && volume != -1) /* none of the 4 entries worked? */
{ /* try "superfloppy" mode */
DEBUGF("No partition found, trying to mount sector 0.\n");
if (!fat_mount(IF_MV2(volume,) IF_MV2(drive,) 0))
{
mounted = 1;
vol_drive[volume] = drive; /* remember the drive for this volume */
}
}
return mounted;
}
struct partinfo* disk_init(IF_MV_NONVOID(int drive))
{
int i;
unsigned char sector[512];
unsigned char* ptr;
#ifdef HAVE_MULTIVOLUME
/* For each drive, start at a different position, in order not to destroy
the first entry of drive 0.
That one is needed to calculate config sector position. */
struct partinfo* pinfo = &part[drive*4];
if ((size_t)drive >= sizeof(part)/sizeof(*part)/4)
return NULL; /* out of space in table */
#else
struct partinfo* pinfo = part;
#endif
rb_fat_read_sectors(IF_MV2(drive,) 0,1,(unsigned short*)(§or));
/* check that the boot sector is initialized */
if ( (sector[510] != 0x55) ||
(sector[511] != 0xaa)) {
DEBUGF("Bad boot sector signature\n");
return NULL;
}
/* parse partitions */
for ( i=0; i<4; i++ ) {
// unsigned char* ptr = sector + 0x1be + 16*i;
ptr = sector + 0x1be + 16*i;
pinfo[i].type = ptr[4];
pinfo[i].start = BYTES2INT32(ptr, 8);
pinfo[i].size = BYTES2INT32(ptr, 12);
DEBUGF("Part%d: Type %02x, start: %08lx size: %08lx\n",
i,pinfo[i].type,pinfo[i].start,pinfo[i].size);
/* extended? */
if ( pinfo[i].type == 5 ) {
/* not handled yet */
}
}
return pinfo;
}
int rb_fat_getc( int fp )
{
char ch;
rb_fat_read( fp, &ch, 1);
return(ch);
}
int rb_fat_disk_mount_all(void)
{
int mounted;
int i;
#if defined(HAVE_MMC) && defined(HAVE_HOTSWAP)
mmc_enable_monitoring(false);
#endif
fat_init(); /* reset all mounted partitions */
for (i=0; i<NUM_VOLUMES; i++)
vol_drive[i] = -1; /* mark all as unassigned */
mounted = disk_mount(0);
#ifdef HAVE_MMC
if (mmc_detect()) /* for Ondio, only if card detected */
{
mounted += disk_mount(1); /* try 2nd "drive", too */
}
#ifdef HAVE_HOTSWAP
mmc_enable_monitoring(true);
#endif
#endif
return mounted;
}
static int flush_cache(int fd)
{
int rc;
struct filedesc* file = &openfiles[fd];
long sector = file->fileoffset / SECTOR_SIZE;
DEBUGF("Flushing dirty sector cache\n");
/* make sure we are on correct sector */
rc = fat_seek(&(file->fatfile), sector);
if ( rc < 0 )
return rc * 10 - 3;
rc = fat_readwrite(&(file->fatfile), 1,
file->cache, true );
if ( rc < 0 ) {
if(file->fatfile.eof)
errno = ENOSPC;
return rc * 10 - 2;
}
file->dirty = false;
return 0;
}
static int rb_fat_readwrite(int fd, void* buf, long count, bool write)
{
long sectors;
long nread=0;
struct filedesc* file = &openfiles[fd];
int rc;
if ( !file->busy ) {
errno = EBADF;
return -1;
}
LDEBUGF( "readwrite(%d,%lx,%ld,%s)\n",
fd,(long)buf,count,write?"write":"read");
/* attempt to read past EOF? */
if (!write && count > file->size - file->fileoffset)
count = file->size - file->fileoffset;
/* any head bytes? */
if ( file->cacheoffset != -1 ) {
int offs = file->cacheoffset;
int headbytes = MIN(count, SECTOR_SIZE - offs);
if (write) {
memcpy( file->cache + offs, buf, headbytes );
file->dirty = true;
}
else {
memcpy( buf, file->cache + offs, headbytes );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -