Rockbox.org home
release
dev builds
extras
themes manual
wiki
device status forums
mailing lists
IRC bugs
patches
dev guide



Rockbox mail archive

Subject: file read/write code
From: Greg Haerr (greg_at_censoft.com)
Date: 2002-11-08


Bjorn,
    I thought I'd send you some code that shows what an
implementation would look like using this buffer-cache idea.
Ultimately, I've realized that this requires some rewriting,
and I'm not sure you're interested in that. The benefits
are simpler code, though, for a filesystem that works for
all read/write/creat/open cases. In addition, for arbitary i/o,
the system uses buffers and buffers files in a superior fashion.
However, the approach is a bit different than your design.
I haven't yet added the optimized case for multi-sector
read/writes.

Let me know what you think of this. I'm doing it for fun,
but I'd certainly like to help Rockbox as well. All the
cacheoffset code, as well as fat_readwrite and
fat_seek, can be removed.

Another point - all code that uses "int" for sectors, clusters,
and positions should be changed to "unsigned long". Take
a look at the gcc -S output and you'll see huge differences
with code speed and size when an unsigned divide or modulo
is computed, versus a signed divide/modulo. I've started
this in this design, in case you're wondering.

Greg

[bio.h]
/* buffer flag bits*/
#define B_NEEDREAD 0x01 /* buffer doesn't contain disk data*/
#define B_DIRTY 0x02 /* buffer needs writing*/
#define B_FAT 0x04 /* buffer is FAT data*/
#define B_DIR 0x08 /* buffer is DIR data*/

#define NOSECTOR ((unsigned long)-1L) /* buffer available*/

struct buffer {
    int flags; /* buffer flags*/
    int lru; /* last accessed timestamp*/
    unsigned long sector; /* disk sector # of data*/
    unsigned char buf[SECTOR_SIZE]; /* buffer data*/
};

[bio.c]
static struct buffer buffers[MAX_BUFFERS];
static int timestamp;

void
initbuffers(void)
{
    struct buffer *bp;

    /* mark all buffers not used*/
    for (bp=buffers; bp<&buffers[MAX_BUFFERS]; ++bp)
        bp->sector = NOSECTOR;
}

/*
 * Write given buffer, return 0 on error.
 */
int
bwrite(struct buffer *bp)
{
    int err;

    DEBUGF("bwrite: sector 0x%x\n", bp->sector);
    err = ata_write_sectors(bp->sector+startsector, 1, bp->buf);
    if (err) {
        DEBUGF("bwrite: error #%d writing sector 0x%x\n", err, bp->sector);
        return 0;
    }
    bp->flags &= ~B_DIRTY;
    return 1;
}

/*
 * Assign a buffer for a given sector.
 */
struct buffer *
getbuf(unsigned long sector)
{
    struct buffer *bp;
    struct buffer *newbp = NULL;

    /* search for cached buffer*/
    for (bp=buffers; bp<&buffers[MAX_BUFFERS]; ++bp) {
        if (bp->sector == sector) {
            bp->lru = ++timestamp;
            return bp;
        }

        /* remember least recently used buffer*/
        if (!newbp || bp->lru < newbp->lru)
            newbp = bp;
    }

    /* not cached, write previous buffer contents*/
    if (newbp->flags & B_DIRTY) {
        if (!bwrite(newbp))
            return NULL;
    }
    
    /* assign new sector to buffer*/
    newbp->flags = B_NEEDREAD;
    newbp->sector = sector;
    newbp->lru = ++timestamp;
    return newbp;
}

/*
 * Read (if necessary) a sector and return a buffer pointer.
 */
struct buffer *
bread(unsigned long sector)
{
    struct buffer *bp;
    int err;

    bp = getbuf(sector);

    if (bp && (bp->flags & B_NEEDREAD)) {
        DEBUGF("bread: read sector 0x%x\n", bp->sector);
        err = ata_read_sectors(sector+startsector, 1, bp->buf);
        if (err) {
            DEBUGF("bread: error #%d reading sector 0x%x\n", err, bp->sector);
            bp->sector = NOSECTOR;
            return NULL;
        }
        bp->flags &= ~B_NEEDREAD;
    }

    return bp;
}

[file.c excerpt]
int
read(int fd, void *buf, int count)
{
    int nread = 0;
    unsigned long blk, offset;
    unsigned long n, left;
    unsigned long sector;
    struct filedesc *f = &openfiles[fd];
    struct buffer *bp;

    if (count == 0)
        return 0;

    do {
        left = f->size - f->fileoffset;
        if (left <= 0)
            break;
        blk = f->fileoffset >> 9;
        offset = f->fileoffset & 511;
        n = min(count, left);
        n = min(512-offset, n);
        if ((sector = fat_bmap(&f->fatfile, blk)) == 0)
            break;
        bp = bread(sector);
        if (!bp) {
            DEBUGF("read: failed reading sector %d\n", sector);
            errno = EIO;
            return -2;
        }
        memcpy(buf, bp->buf+offset, n);
        count -= n;
        nread += n;
        ((char *)buf) += n;
        f->fileoffset += n;
    } while (count != 0);

    return nread;
}

int
write(int fd, void *buf, int count)
{
    int nwritten = 0;
    unsigned long blk, offset;
    unsigned long n;
    unsigned long sector;
    struct filedesc *f = &openfiles[fd];
    struct buffer *bp;

    if (count == 0)
        return 0;

    do {
        blk = f->fileoffset >> 9;
        offset = f->fileoffset & 511;
        n = min(512-offset, count);
        if ((sector = fat_bmap(&f->fatfile, blk)) == 0)
            break; // FIXME: return error?
        if (n == 512)
            bp = getbuf(sector);
        else
            bp = bread(sector);
        if (!bp) {
            DEBUGF("write: failed reading sector %d\n", sector);
            errno = EIO;
            return -2;
        }
        memcpy(bp->buf+offset, buf, n);
        bp->flags |= B_DIRTY;
        count -= n;
        nwritten += n;
        ((char *)buf) += n;
        f->fileoffset += n;
        if (f->fileoffset > f->size)
            f->size = f->fileoffset;
    } while (count != 0);

    return nwritten;
}

// FIXME unsigned long
int
lseek(int fd, int offset, int whence)
{
    int pos;

    LDEBUGF("lseek(%d,%d,%d)\n",fd,offset,whence);

    if ( !openfiles[fd].busy ) {
        errno = EBADF;
        return -1;
    }

    switch ( whence ) {
        case SEEK_SET:
            pos = offset;
            break;

        case SEEK_CUR:
            pos = openfiles[fd].fileoffset + offset;
            break;

        case SEEK_END:
            pos = openfiles[fd].size + offset;
            break;

        default:
            errno = EINVAL;
            return -2;
    }

    if (pos > openfiles[fd].size) {
        errno = EINVAL;
        return -3;
    }

    openfiles[fd].fileoffset = pos;
    return pos;
}

[fat.c]
[remove fat_readwrite, fat_seek]
/*
 * Map a FAT file logical block number into a disk sector number.
 */
unsigned long
fat_bmap(struct fat_file *file, unsigned long blk)
{
    unsigned long cluster = file->firstcluster;
    unsigned long nextcluster;
    long clusternum, i;
    int sector, tmp;

    sector = blk % fat_bpb.bpb_secperclus;

    if (!cluster || blk >= fat_bpb.bpb_secperclus) {
        clusternum = blk / fat_bpb.bpb_secperclus;
        if (!cluster)
            ++clusternum;
        for (i=0; i<clusternum; ++i) {
            nextcluster = get_next_cluster(cluster);
            if (!nextcluster) {
                /* not found, must be writing*/
                nextcluster = next_write_cluster(file, cluster, &tmp);
                if (!nextcluster)
                    return 0;
            }
            cluster = nextcluster;
        }
    }

    /* save lastcluster for close time truncation*/
    file->lastcluster = cluster;

    // FIXME save clusternum and cluster here for speed
    return cluster2sec(cluster) + sector;
}



Page was last modified "Jan 10 2012" The Rockbox Crew
aaa