#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);
}
|