Subversion Repositories planix.SVN

Rev

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

#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "flashfs.h"

enum
{
        debug   = 0,
        diags   = 1,
};

typedef struct  Gen     Gen;
typedef struct  Sect    Sect;

struct Gen
{
        int     gnum;
        int     count;
        int     low;
        int     high;
        Sect*   head;
        Sect*   tail;
        Sect*   dup;
        Sect**  vect;
};

struct Sect
{
        int     sect;
        ulong   seq;
        int     coff;
        int     toff;
        int     sum;
        ulong   time;
        Sect*   next;
};

static  Gen     gens[2];
static  Sect*   freehead;
static  Sect*   freetail;
static  int     nfree;
static  int     nbad;
static  ulong   ltime;
static  int     cursect;

/*
 *      If we have a delta then file times are in the future.
 *      But they drift back to reality.
 */
ulong
now(void)
{
        ulong cur, drift;
        static ulong last;

        cur = time(0);
        if(cur < last)
                delta += last - cur;
        else if(delta != 0 && last != 0) {
                drift = (cur - last + 1) / 2;
                if(drift > delta)
                        delta = 0;
                else
                        delta -= drift;
        }
        last = cur;
        return cur + delta;
}

static void
damaged(char *mesg)
{
        sysfatal("damaged filesystem: %s", mesg);
}

static void
lddisc(char *mesg)
{
        if(debug)
                fprint(2, "discard %s\n", mesg);
        else
                USED(mesg);
}

static Sect *
getsect(void)
{
        Sect *t;

        t = freehead;
        freehead = t->next;
        if(freehead == nil)
                freetail = nil;
        nfree--;
        return t;
}

static void
newsect(Gen *g, Sect *s)
{
        int m, n, err;
        uchar hdr[2*3];

        if(debug)
                fprint(2, "new %d %ld\n", g->gnum, s->seq);
        if(g->tail == nil)
                g->head = s;
        else
                g->tail->next = s;
        g->tail = s;
        s->next = nil;
        m = putc3(&hdr[0], g->gnum);
        n = putc3(&hdr[m], s->seq);
        s->toff = MAGSIZE + m + n;
        s->coff = s->toff + 4;
        s->time = NOTIME;
        s->sum = 0;
        err = 1;
        for(;;) {
                if(writedata(err, s->sect, hdr, m + n, MAGSIZE)
                && writedata(err, s->sect, magic, MAGSIZE, 0))
                        break;
                clearsect(s->sect);
                err = 0;
        }
}

static Sect *
newsum(Gen *g, ulong seq)
{
        Sect *t;

        if(nfree == 0)
                damaged("no free sector for summary");
        t = getsect();
        t->seq = seq;
        newsect(g, t);
        return t;
}

static void
freesect(Sect *s)
{
        clearsect(s->sect);
        if(freetail == nil)
                freehead = s;
        else
                freetail->next = s;
        freetail = s;
        s->next = nil;
        nfree++;
}

static void
dupsect(Sect *s, int renum)
{
        Sect *t;
        Renum r;
        uchar *b;
        int err, n;
        ulong doff, off;

        if(nfree == 0)
                damaged("no free for copy");
        t = getsect();
        b = sectbuff;
        off = s->coff;
        readdata(s->sect, b, off, 0);
        doff = s->toff;
        if(s->time == NOTIME)
                doff += 4;
        // Window 5
        err = 1;
        for(;;) {
                if(writedata(err, t->sect, b + 1, s->toff - 1, 1)
                && writedata(err, t->sect, b + doff, off - doff, doff)
                && writedata(err, t->sect, b, 1, 0))
                        break;
                clearsect(t->sect);
                err = 0;
        }
        if(renum) {
                r.old = s->sect;
                r.new = t->sect;
                erenum(&r);
        }
        n = s->sect;
        s->sect = t->sect;
        t->sect = n;
        freesect(t);
        if(cursect >= 0)
                readdata(cursect, b, sectsize, 0);
}

static void
gswap(void)
{
        Gen t;

        t = gens[0];
        gens[0] = gens[1];
        gens[1] = t;
}

static void
checkdata(void)
{
        Gen *g;

        g = &gens[0];
        if(g->dup != nil) {     // Window 5 damage
                if(diags)
                        fprint(2, "%s: window 5 damage\n", argv0);
                freesect(g->dup);
                g->dup = nil;
        }
}

static void
checksweep(void)
{
        Gen *g;
        Jrec j;
        uchar *b;
        int n, op;
        Sect *s, *t, *u;
        long off, seq, soff;

        g = &gens[1];
        if(g->dup != nil) {     // Window 5 damage
                if(diags)
                        fprint(2, "%s: window 5 damage\n", argv0);
                freesect(g->dup);
                g->dup = nil;
        }

        s = g->head;
        if(s != g->tail) {
                while(s->next != g->tail)
                        s = s->next;
        }

        b = sectbuff;
        op = -1;
        seq = -1;
        soff = 0;
        u = nil;
        t = s;
        for(;;) {
                readdata(t->sect, b, sectsize, 0);
                off = t->toff + 4;
                for(;;) {
                        n = convM2J(&j, &b[off]);
                        if(n < 0) {
                                if(j.type != 0xFF) {
                                        if(debug)
                                                fprint(2, "s[%d] @ %d %ld\n", j.type, t->sect, off);
                                        damaged("bad Jrec");
                                }
                                break;
                        }
                        switch(j.type) {
                        case FT_SUMMARY:
                        case FT_SUMBEG:
                                seq = j.seq;
                        case FT_SUMEND:
                                op = j.type;
                                soff = off;
                                u = t;
                                break;
                        case FT_WRITE:
                        case FT_AWRITE:
                                off += j.size;
                        }
                        off += n;
                }
                t = t->next;
                if(t == nil)
                        break;
        }

        if(op == FT_SUMBEG) {           // Window 1 damage
                if(diags)
                        fprint(2, "%s: window 1 damage\n", argv0);
                if(u != s) {
                        freesect(u);
                        s->next = nil;
                        g->tail = s;
                }
                s->coff = soff;
                dupsect(s, 0);
                seq--;
        }

        if(seq >= 0) {
                g = &gens[0];
                if(seq > g->low)
                        damaged("high sweep");
                if(seq == g->low) {     // Window 2 damage
                        if(diags)
                                fprint(2, "%s: window 2 damage\n", argv0);
                        s = g->head;
                        g->head = s->next;
                        freesect(s);
                        if(g->head == nil) {
                                g->tail = nil;
                                g->gnum += 2;
                                newsum(g, 0);
                                gswap();
                        }
                }
        }
}

void
load1(Sect *s, int parity)
{
        int n;
        Jrec j;
        uchar *b;
        char *err;
        Extent *x;
        Entry *d, *e;
        ulong ctime, off, mtime;

        if(s->sect < 0 && readonly)     // readonly damaged
                return;

        b = sectbuff;
        readdata(s->sect, b, sectsize, 0);
        s->sum = 0;
        off = s->toff;
        ctime = get4(&b[off]);
        off += 4;

        for(;;) {
                n = convM2J(&j, &b[off]);
                if(n < 0) {
                        if(j.type != 0xFF) {
                                if(debug)
                                        fprint(2, "l[%d] @  %d %ld\n", j.type, s->sect, off);
                                damaged("bad Jrec");
                        }
                        s->coff = off;
                        break;
                }
                off += n;
                if(debug)
                        fprint(2, "get %J\n", &j);
                switch(j.type) {
                case FT_create:
                        ctime += j.mtime;
                create:
                        d = elookup(j.parent);
                        if(d == nil) {
                                lddisc("parent");
                                break;
                        }
                        d->ref++;
                        e = ecreate(d, j.name, j.fnum, j.mode, ctime, &err);
                        if(e == nil) {
                                d->ref--;
                                lddisc("create");
                                break;
                        }
                        e->ref--;
                        if(j.type == FT_trunc)
                                e->mode = j.mode;
                        break;
                case FT_chmod:
                        e = elookup(j.fnum);
                        if(e == nil) {
                                lddisc("lookup");
                                break;
                        }
                        echmod(e, j.mode, j.mnum);
                        break;
                case FT_REMOVE:
                        e = elookup(j.fnum);
                        if(e == nil) {
                                lddisc("lookup");
                                break;
                        }
                        if(eremove(e) != nil) {
                                lddisc("remove");
                                break;
                        }
                        break;
                case FT_AWRITE:
                        mtime = 0;
                        goto write;
                case FT_WRITE:
                        ctime += j.mtime;
                        mtime = ctime;
                write:
                        x = emalloc9p(sizeof(Extent));
                        x->sect = s->sect;
                        x->addr = off;
                        x->off = j.offset;
                        x->size = j.size;
                        off += j.size;
                        e = elookup(j.fnum);
                        if(e == nil) {
                                lddisc("lookup");
                                break;
                        }
                        ewrite(e, x, parity, mtime);
                        break;
                case FT_trunc:
                        ctime += j.mtime;
                        e = elookup(j.tnum);
                        if(e == nil) {
                                if(debug)
                                        fprint(2, "-> create\n");
                                goto create;
                        }
                        etrunc(e, j.fnum, ctime);
                        break;
                case FT_SUMMARY:
                case FT_SUMBEG:
                case FT_SUMEND:
                        s->sum += n;
                        break;
                default:
                        damaged("load1 botch");
                }
        }

        if(s->sum > Nsum)
                s->sum = Nsum;

        s->time = ctime;
        if(ctime != NOTIME && ctime > ltime)
                ltime = ctime;
}

void
load0(int parity)
{
        Sect *s;

        if(debug)
                fprint(2, "load %d\n", parity);
        for(s = gens[parity].head; s != nil; s = s->next)
                load1(s, parity);
}

void
loadfs(int ro)
{
        Gen *g;
        Sect *s;
        ulong u, v;
        int i, j, n;
        uchar hdr[MAXHDR];

        readonly = ro;
        fmtinstall('J', Jconv);

        for(i = 0; i < 2; i++) {
                g = &gens[i];
                g->gnum = -1;
                g->low = nsects;
                g->high = -1;
                g->count = 0;
                g->head = nil;
                g->tail = nil;
        }

        for(i = 0; i < nsects; i++) {
                readdata(i, hdr, MAXHDR, 0);
                if(memcmp(hdr, magic, MAGSIZE) == 0) {
                        n = MAGSIZE + getc3(&hdr[MAGSIZE], &u);
                        getc3(&hdr[n], &v);
                        if(debug)
                                fprint(2, "%d: %ld %ld\n", i, u, v);
                        for(j = 0; j < 2; j++) {
                                g = &gens[j];
                                if(g->gnum == u) {
                                        g->count++;
                                        if(v < g->low)
                                                g->low = v;
                                        if(v > g->high)
                                                g->high = v;
                                        break;
                                }
                                else if(g->gnum < 0) {
                                        g->gnum = u;
                                        g->count = 1;
                                        g->low = v;
                                        g->high = v;
                                        break;
                                }
                        }
                        if(j == 2)
                                damaged("unexpected generation");
                }
                else if(hdr[0] == 0xFF) {
                        nfree++;
                        s = emalloc9p(sizeof(Sect));
                        s->sect = i;
                        s->next = freehead;
                        freehead = s;
                        if(freetail == nil)
                                freetail = s;
                }
                else
                        nbad++;
        }

        if(nbad > 0)
                damaged("bad sectors");

        if(gens[0].gnum < 0)
                damaged("no data");

        if(gens[1].gnum < 0) {          // Window 3 death
                if(diags)
                        fprint(2, "%s: window 3 damage\n", argv0);
                g = &gens[1];
                g->gnum = gens[0].gnum + 1;
                g->low = 0;
                g->high = 0;
                g->count = 1;
                if(!readonly)
                        newsum(g, 0);
        }

        if(gens[0].gnum > gens[1].gnum)
                gswap();

        for(i = 0; i < 2; i++) {
                g = &gens[i];
                n = g->count;
                if(n <= g->high - g->low)
                        damaged("missing sectors");
                g->vect = emalloc9p(n * sizeof(Sect *));
                for(j = 0; j < n; j++) {
                        s = emalloc9p(sizeof(Sect));
                        s->sect = -1;
                        g->vect[j] = s;
                }
        }

        if(debug) {
                for(j = 0; j < 2; j++) {
                        g = &gens[j];
                        print("%d\t%d\t%d-%d\n", g->gnum, g->count, g->low, g->high);
                }
        }

        for(i = 0; i < nsects; i++) {
                readdata(i, hdr, MAXHDR, 0);
                if(memcmp(hdr, magic, MAGSIZE) == 0) {
                        n = MAGSIZE + getc3(&hdr[MAGSIZE], &u);
                        n += getc3(&hdr[n], &v);
                        g = nil;
                        for(j = 0; j < 2; j++) {
                                g = &gens[j];
                                if(g->gnum == u)
                                        break;
                        }
                        if(j == 2)
                                damaged("generation botch");
                        s = g->vect[v - g->low];
                        s->seq = v;
                        s->toff = n;
                        if(s->sect < 0)
                                s->sect = i;
                        else if(v == g->high && g->dup == nil) {
                                s = emalloc9p(sizeof(Sect));
                                s->sect = i;
                                g->dup = s;
                        }
                        else
                                damaged("dup block");
                }
        }

        for(j = 0; j < 2; j++) {
                g = &gens[j];
                for(i = 0; i < g->count; i++) {
                        s = g->vect[i];
                        if(g->tail == nil)
                                g->head = s;
                        else
                                g->tail->next = s;
                        g->tail = s;
                        s->next = nil;
                }
                free(g->vect);
        }

        cursect = -1;

        if(!readonly) {
                checkdata();
                checksweep();
        }

        load0(1);
        load0(0);

        if(ltime != 0) {
                u = now();
                if(u < ltime) {
                        delta = ltime - u;
                        if(diags)
                                fprint(2, "%s: check clock: delta = %lud\n", argv0, delta);
                }
        }

        limit = 80 * nsects * sectsize / 100;
        maxwrite = sectsize - MAXHDR - Nwrite - Nsum;
        if(maxwrite > WRSIZE)
                maxwrite = WRSIZE;
}

static int
sputw(Sect *s, Jrec *j, int mtime, Extent *x, void *a)
{
        ulong t;
        int err, n, r;
        uchar buff[Nmax], type[1];

        if(debug)
                fprint(2, "put %J\n", j);

        if(mtime) {
                t = j->mtime;
                if(s->time == NOTIME) {
                        put4(buff, t);
                        if(!writedata(1, s->sect, buff, 4, s->toff)) {
                                dupsect(s, 1);
                                writedata(0, s->sect, buff, 4, s->toff);
                        }
                        s->time = t;
                        j->mtime = 0;
                }
                else {
                        j->mtime = t - s->time;
                        s->time = t;
                }
        }

        n = convJ2M(j, buff);
        if(n < 0)
                damaged("convJ2M");

        // Window 4
        err = 2;
        for(;;) {
                err--;
                if(!err)
                        dupsect(s, 1);  // Window 4 damage
                t = s->coff + 1;
                if(!writedata(err, s->sect, buff, n, t))
                        continue;

                t += n;
                r = n;
                if(x != nil) {
                        x->sect = s->sect;
                        x->addr = t;
                        if(!writedata(err, s->sect, a, j->size, t))
                                continue;
                        t += j->size;
                        r += j->size;
                }

                type[0] = j->type;
                if(!writedata(err, s->sect, type, 1, s->coff))
                        continue;
                r++;
                break;
        }
        s->coff = t;
        return r;
}

static void
summarize(void)
{
        Gen *g;
        uchar *b;
        Entry *e;
        Extent *x;
        Jrec j, sum;
        Sect *s, *t;
        ulong off, ctime;
        int n, bytes, more, mtime, sumd;

        g = &gens[eparity];
        s = g->head;
        g->head = s->next;
        if(g->head == nil)
                g->tail = nil;
        g = &gens[eparity^1];
        t = g->tail;
        b = sectbuff;
        x = nil;

        if(debug)
                fprint(2, "summarize\n");

        for(;;) {       // Window 1
                readdata(s->sect, b, sectsize, 0);
                off = s->toff;
                ctime = get4(&b[off]);
                off += 4;
                sumd = 0;

                cursect = s->sect;
                while(b[off] != 0xFF) {
                        n = convM2J(&j, &b[off]);
                        if(n < 0)
                                damaged("bad Jrec");
                        if(debug)
                                fprint(2, "read %J\n", &j);
                        off += n;
                        bytes = n;
                        mtime = 0;
                        switch(j.type) {
                        case FT_create:
                                ctime += j.mtime;
                                mtime = 1;
                        create:
                                e = elookup(j.fnum);
                                if(e == nil)
                                        continue;
                                break;
                        case FT_chmod:
                                e = elookup(j.fnum);
                                if(e == nil || j.mnum != e->mnum)
                                        continue;
                                break;
                        case FT_REMOVE:
                                e = elookup(j.fnum);
                                if(e == nil)
                                        continue;
                                break;
                        case FT_trunc:
                                ctime += j.mtime;
                                mtime = 1;
                                e = elookup(j.tnum);
                                if(e == nil) {
                                        if(debug)
                                                fprint(2, "-> create\n");
                                        j.type = FT_create;
                                        goto create;
                                }
                                break;
                        case FT_AWRITE:
                                goto write;
                        case FT_WRITE:
                                ctime += j.mtime;
                                mtime = 1;
                        write:
                                e = elookup(j.fnum);
                                if(e == nil) {
                                        off += j.size;
                                        continue;
                                }
                                x = esum(e, s->sect, off, &more);
                                if(x == nil) {
                                        damaged("missing extent");
                                        off += j.size;
                                        continue;
                                }
                                if(more) {
                                        j.type = FT_AWRITE;
                                        mtime = 0;
                                }
                                bytes += j.size;
                                break;
                        case FT_SUMMARY:
                        case FT_SUMBEG:
                        case FT_SUMEND:
                                continue;
                        default:
                                damaged("summarize botch");
                        }

                        if(!sumd) {
                                if(t->coff + Nsumbeg >= sectsize - 1)
                                        t = newsum(g, t->seq + 1);
                                sum.type = FT_SUMBEG;
                                sum.seq = s->seq;
                                if(debug)
                                        fprint(2, "+ %J\n", &sum);
                                t->sum += sputw(t, &sum, 0, nil, nil);
                                if(t->sum >= Nsum)
                                        t->sum = Nsum;
                                sumd = 1;
                        }

                        if(t->coff + bytes >= sectsize - Nsum + t->sum - 1)
                                t = newsum(g, t->seq + 1);

                        if(mtime)
                                j.mtime = ctime;

                        switch(j.type) {
                        case FT_AWRITE:
                        case FT_WRITE:
                                sputw(t, &j, mtime, x, &b[off]);
                                off += j.size;
                                break;
                        default:
                                sputw(t, &j, mtime, nil, nil);
                        }
                }
                cursect = -1;

                if(t->coff + Nsumbeg >= sectsize - 1)
                        t = newsum(g, t->seq + 1);
                if(sumd)
                        sum.type = FT_SUMEND;
                else {
                        sum.type = FT_SUMMARY;
                        sum.seq = s->seq;
                }
                if(debug)
                        fprint(2, "+ %J\n", &sum);
                t->sum += sputw(t, &sum, 0, nil, nil);
                if(t->sum >= Nsum)
                        t->sum = Nsum;

                // Window 2
                freesect(s);
                g = &gens[eparity];
                s = g->head;
                if(s == nil) {
                        // Window 3
                        g->gnum += 2;
                        newsum(g, 0);
                        eparity ^= 1;
                        return;
                }

                if(nfree >= Nfree)
                        return;

                g->head = s->next;
                if(g->head == nil)
                        g->tail = nil;
                g = &gens[eparity^1];
        }
}

char *
need(int bytes)
{
        Gen *g;
        int sums;
        Sect *s, *t;

        sums = 0;
        for(;;) {
                s = gens[eparity].tail;
                if(s->coff + bytes < sectsize - Nsum + s->sum - 1)
                        return nil;

                if(nfree >= Nfree)
                        break;

                if(sums == 2) {
                        readonly = 1;
                        return "generation full";
                }

                summarize();
                sums++;
        }

        g = &gens[eparity];
        t = getsect();
        t->seq = g->tail->seq + 1;
        newsect(g, t);
        return nil;
}

void
putw(Jrec *j, int mtime, Extent *x, void *a)
{
        sputw(gens[eparity].tail, j, mtime, x, a);
}

void
put(Jrec *j, int mtime)
{
        sputw(gens[eparity].tail, j, mtime, nil, nil);
}