Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include "cformat.h"
#include "lru.h"
#include "bcache.h"
#include "disk.h"
#include "inode.h"
#include "file.h"

/*
 *  merge data with that which already exists in a block
 *
 *  we allow only one range per block, always use the new
 *  data if the ranges don't overlap.
 */
void
fmerge(Dptr *p, char *to, char *from, int start, int len)
{
        int end;

        end = start + len;
        memmove(to+start, from, end-start);

        /*
         *  if ranges do not overlap...
         */
        if(start>p->end || p->start>end){
                /*
                 *  just use the new data
                 */
                p->start = start;
                p->end = end;
        } else {
                /*
                 *  merge ranges
                 */
                if(start < p->start)
                        p->start = start;
                if(end > p->end)
                        p->end = end;
        }

}

/*
 *  write a block (or less) of data onto a disk, follow it with any necessary
 *  pointer writes.
 *
 *      N.B. ordering is everything
 */
int
fbwrite(Icache *ic, Ibuf *b, char *a, ulong off, int len)
{
        int wrinode;
        ulong fbno;
        Bbuf *dbb;      /* data block */
        Bbuf *ibb;      /* indirect block */
        Dptr *p;
        Dptr t;

        fbno = off / ic->bsize;
        p = &b->inode.ptr;
        ibb = 0;
        wrinode = 0;

        /*
         *  are there any pages for this inode?
         */
        if(p->bno == Notabno){
                wrinode = 1;
                goto dowrite;
        }
        
        /*
         *  is it an indirect block?
         */
        if(p->bno & Indbno){
                ibb = bcread(ic, p->bno);
                if(ibb == 0)
                        return -1;
                p = (Dptr*)ibb->data;
                p += fbno % ic->p2b;
                goto dowrite;
        }

        /*
         *  is it the wrong direct block?
         */
        if((p->fbno%ic->p2b) != (fbno%ic->p2b)){
                /*
                 *  yes, make an indirect block
                 */
                t = *p;
                dpalloc(ic, p);
                if(p->bno == Notabno){
                        *p = t;
                        return -1;
                }
                ibb = bcalloc(ic, p->bno);
                if(ibb == 0){
                        *p = t;
                        return -1;
                }
                p = (Dptr*)ibb->data;
                p += t.fbno % ic->p2b;
                *p = t;
                p = (Dptr*)ibb->data;
                p += fbno % ic->p2b;
        }
        wrinode = 1;

dowrite:
        /*
         *  get the data block into the block cache
         */
        if(p->bno == Notabno){
                /*
                 *  create a new block
                 */
                dalloc(ic, p);
                if(p->bno == Notabno)
                        return -1;              /* no blocks left (maybe) */
                dbb = bcalloc(ic, p->bno);
        } else {
                /*
                 *  use what's there
                 */
                dbb = bcread(ic, p->bno);
        }
        if(dbb == 0)
                return -1;

        /*
         *  merge in the new data
         */
        if(p->fbno != fbno){
                p->start = p->end = 0;
                p->fbno = fbno;
        }
        fmerge(p, dbb->data, a, off % ic->bsize, len);

        /*
         *  write changed blocks back in the
         *  correct order
         */
        bcmark(ic, dbb);
        if(ibb)
                bcmark(ic, ibb);
        if(wrinode)
                if(iwrite(ic, b) < 0)
                        return -1;
        return len;
}

/*
 *  write `n' bytes to the cache
 *
 *  return number of bytes written
 */
long
fwrite(Icache *ic, Ibuf *b, char *a, ulong off, long n)
{
        int len;
        long sofar;

        for(sofar = 0; sofar < n; sofar += len){
                len = ic->bsize - ((off+sofar)%ic->bsize);
                if(len > n - sofar)
                        len = n - sofar;
                if(fbwrite(ic, b, a+sofar, off+sofar, len) < 0)
                        return sofar;
        }
        return sofar;
}

/*
 *  get a pointer to the next valid data at or after `off'
 */
Dptr *
fpget(Icache *ic, Ibuf *b, ulong off)
{
        ulong fbno;
        long doff;
        Bbuf *ibb;      /* indirect block */
        Dptr *p, *p0, *pf;

        fbno = off / ic->bsize;
        p = &b->inode.ptr;

        /*
         *  are there any pages for this inode?
         */
        if(p->bno == Notabno)
                return 0;
        
        /*
         *  if it's a direct block, life is easy?
         */
        if(!(p->bno & Indbno)){
                /*
                 *  a direct block, return p if it's at least past what we want
                 */
                if(p->fbno > fbno)
                        return p;
                if(p->fbno < fbno)
                        return 0;
                doff = off % ic->bsize;
                if(doff>=p->start && doff<p->end)
                        return p;
                else
                        return 0;
        }

        /*
         *  read the indirect block
         */
        ibb = bcread(ic, p->bno);
        if(ibb == 0)
                return 0;

        /*
         *  find the next valid pointer
         */
        p0 = (Dptr*)ibb->data;
        pf = p0 + (fbno % ic->p2b);
        if(pf->bno!=Notabno && pf->fbno==fbno){
                doff = off % ic->bsize;
                if(doff<pf->end)
                        return pf;
        }
        for(p = pf+1; p < p0 + ic->p2b; p++){
                fbno++;
                if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
                        return p;
        }
        for(p = p0; p < pf; p++){
                fbno++;
                if(p->fbno==fbno && p->bno!=Notabno && p->start<p->end)
                        return p;
        }
        return 0;
}

/*
 *  read `n' bytes from the cache.
 *
 *  if we hit a gap and we've read something,
 *  return number of bytes read so far.
 *
 *  if we start with a gap, return minus the number of bytes
 *  to the next data.
 *
 *  if there are no bytes cached, return 0.
 */
long
fread(Icache *ic, Ibuf *b, char *a, ulong off, long n)
{
        int len, start;
        long sofar, gap;
        Dptr *p;
        Bbuf *bb;

        for(sofar = 0; sofar < n; sofar += len, off += len){
                /*
                 *  get pointer to next data
                 */
                len = n - sofar;
                p = fpget(ic, b, off);

                /*
                 *  if no more data, return what we have so far
                 */
                if(p == 0)
                        return sofar;

                /*
                 *  if there's a gap, return the size of the gap
                 */
                gap = (ic->bsize*p->fbno + p->start) - off;
                if(gap>0)
                        if(sofar == 0)
                                return -gap;
                        else
                                return sofar;

                /*
                 *  return what we have
                 */
                bb = bcread(ic, p->bno);
                if(bb == 0)
                        return sofar;
                start = p->start - gap;
                if(p->end - start < len)
                        len = p->end - start;
                memmove(a + sofar, bb->data + start, len);
        }
        return sofar;
}