⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fat_file.c

📁 This SPI-mode SD Card controller is a free SOPC Builder component that can be used in any SOPC Build
💻 C
📖 第 1 页 / 共 2 页
字号:
/***************************************************************************
 *             __________               __   ___.
 *   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*)(&sector));

    /* 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 + -