xref: /inferno-os/os/port/devtinyfs.c (revision 9dc22068e29604f4b484e746112a9a4efe6fd57f)
1  /*
2   *  a pity the code isn't also tiny...
3   */
4  #include "u.h"
5  #include "../port/lib.h"
6  #include "../port/error.h"
7  #include "mem.h"
8  #include "dat.h"
9  #include "fns.h"
10  
11  enum{
12  	Qdir,
13  	Qmedium,
14  
15  	Maxfs=		10,	/* max file systems */
16  
17  	Blen=		48,	/* block length */
18  	Nlen=		28,	/* name length */
19  	Dlen=		Blen - 4,
20  
21  	Tagdir=		'd',
22  	Tagdata=	'D',
23  	Tagend=		'e',
24  	Tagfree=	'f',
25  
26  	Notapin=		0xffff,
27  	Notabno=		0xffff,
28  
29  	Fcreating=	1,
30  	Frmonclose=	2
31  };
32  
33  /* representation of a Tdir on medium */
34  typedef struct Mdir Mdir;
35  struct Mdir {
36  	uchar	type;
37  	uchar	bno[2];
38  	uchar	pin[2];
39  	char	name[Nlen];
40  	char	pad[Blen - Nlen - 6];
41  	uchar	sum;
42  };
43  
44  /* representation of a Tdata/Tend on medium */
45  typedef struct Mdata Mdata;
46  struct Mdata {
47  	uchar	type;
48  	uchar	bno[2];
49  	uchar	data[Dlen];
50  	uchar	sum;
51  };
52  
53  typedef struct Tfile Tfile;
54  struct Tfile {
55  	int	r;
56  	char	name[Nlen];
57  	ushort	bno;
58  	ushort	dbno;
59  	ushort	pin;
60  	uchar	flag;
61  	ulong	length;
62  
63  	/* hint to avoid egregious reading */
64  	ushort	fbno;
65  	ulong	finger;
66  };
67  
68  typedef struct Tfs Tfs;
69  struct Tfs {
70  	QLock	ql;
71  	int	r;
72  	Chan	*c;
73  	uchar	*map;
74  	int	nblocks;
75  	Tfile	*f;
76  	int	nf;
77  	int	fsize;
78  };
79  
80  static struct {
81  	Tfs	fs[Maxfs];
82  } tinyfs;
83  
84  #define GETS(x) ((x)[0]|((x)[1]<<8))
85  #define PUTS(x, v) {(x)[0] = (v);(x)[1] = ((v)>>8);}
86  
87  #define GETL(x) (GETS(x)|(GETS(x+2)<<16))
88  #define PUTL(x, v) {PUTS(x, v);PUTS(x+2, (v)>>16)};
89  
90  static uchar
91  checksum(uchar *p)
92  {
93  	uchar *e;
94  	uchar s;
95  
96  	s = 0;
97  	for(e = p + Blen; p < e; p++)
98  		s += *p;
99  	return s;
100  }
101  
102  static void
103  mapclr(Tfs *fs, ulong bno)
104  {
105  	fs->map[bno>>3] &= ~(1<<(bno&7));
106  }
107  
108  static void
109  mapset(Tfs *fs, ulong bno)
110  {
111  	fs->map[bno>>3] |= 1<<(bno&7);
112  }
113  
114  static int
115  isalloced(Tfs *fs, ulong bno)
116  {
117  	return fs->map[bno>>3] & (1<<(bno&7));
118  }
119  
120  static int
121  mapalloc(Tfs *fs)
122  {
123  	int i, j, lim;
124  	uchar x;
125  
126  	lim = (fs->nblocks + 8 - 1)/8;
127  	for(i = 0; i < lim; i++){
128  		x = fs->map[i];
129  		if(x == 0xff)
130  			continue;
131  		for(j = 0; j < 8; j++)
132  			if((x & (1<<j)) == 0){
133  				fs->map[i] = x|(1<<j);
134  				return i*8 + j;
135  			}
136  	}
137  
138  	return Notabno;
139  }
140  
141  static Mdir*
142  validdir(Tfs *fs, uchar *p)
143  {
144  	Mdir *md;
145  	ulong x;
146  
147  	if(checksum(p) != 0)
148  		return 0;
149  	if(p[0] != Tagdir)
150  		return 0;
151  	md = (Mdir*)p;
152  	x = GETS(md->bno);
153  	if(x >= fs->nblocks)
154  		return 0;
155  	return md;
156  }
157  
158  static Mdata*
159  validdata(Tfs *fs, uchar *p, int *lenp)
160  {
161  	Mdata *md;
162  	ulong x;
163  
164  	if(checksum(p) != 0)
165  		return 0;
166  	md = (Mdata*)p;
167  	switch(md->type){
168  	case Tagdata:
169  		x = GETS(md->bno);
170  		if(x >= fs->nblocks)
171  			return 0;
172  		if(lenp)
173  			*lenp = Dlen;
174  		break;
175  	case Tagend:
176  		x = GETS(md->bno);
177  		if(x > Dlen)
178  			return 0;
179  		if(lenp)
180  			*lenp = x;
181  		break;
182  	default:
183  		return 0;
184  	}
185  	return md;
186  }
187  
188  static Mdata*
189  readdata(Tfs *fs, ulong bno, uchar *buf, int *lenp)
190  {
191  	if(bno >= fs->nblocks)
192  		return 0;
193  	if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen)
194  		error(Eio);
195  	return validdata(fs, buf, lenp);
196  }
197  
198  static void
199  writedata(Tfs *fs, ulong bno, ulong next, uchar *buf, int len, int last)
200  {
201  	Mdata md;
202  
203  	if(bno >= fs->nblocks)
204  		error(Eio);
205  	if(len > Dlen)
206  		len = Dlen;
207  	if(len < 0)
208  		error(Eio);
209  	memset(&md, 0, sizeof(md));
210  	if(last){
211  		md.type = Tagend;
212  		PUTS(md.bno, len);
213  	} else {
214  		md.type = Tagdata;
215  		PUTS(md.bno, next);
216  	}
217  	memmove(md.data, buf, len);
218  	md.sum = 0 - checksum((uchar*)&md);
219  
220  	if(devtab[fs->c->type]->write(fs->c, &md, Blen, Blen*bno) != Blen)
221  		error(Eio);
222  }
223  
224  static void
225  writedir(Tfs *fs, Tfile *f)
226  {
227  	Mdir *md;
228  	uchar buf[Blen];
229  
230  	if(f->bno == Notabno)
231  		return;
232  
233  	md = (Mdir*)buf;
234  	memset(buf, 0, Blen);
235  	md->type = Tagdir;
236  	strncpy(md->name, f->name, sizeof(md->name)-1);
237  	PUTS(md->bno, f->dbno);
238  	PUTS(md->pin, f->pin);
239  	md->sum = 0 - checksum(buf);
240  
241  	if(devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno) != Blen)
242  		error(Eio);
243  }
244  
245  static void
246  freeblocks(Tfs *fs, ulong bno, ulong bend)
247  {
248  	uchar buf[Blen];
249  	Mdata *md;
250  
251  	if(waserror())
252  		return;
253  
254  	while(bno != bend && bno != Notabno){
255  		mapclr(fs, bno);
256  		if(devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno) != Blen)
257  			break;
258  		md = validdata(fs, buf, 0);
259  		if(md == 0)
260  			break;
261  		if(md->type == Tagend)
262  			break;
263  		bno = GETS(md->bno);
264  	}
265  
266  	poperror();
267  }
268  
269  static void
270  freefile(Tfs *fs, Tfile *f, ulong bend)
271  {
272  	uchar buf[Blen];
273  
274  	/* remove blocks from map */
275  	freeblocks(fs, f->dbno, bend);
276  
277  	/* change file type to free on medium */
278  	if(f->bno != Notabno){
279  		memset(buf, 0x55, Blen);
280  		devtab[fs->c->type]->write(fs->c, buf, Blen, Blen*f->bno);
281  		mapclr(fs, f->bno);
282  	}
283  
284  	/* forget we ever knew about it */
285  	memset(f, 0, sizeof(*f));
286  }
287  
288  static void
289  expand(Tfs *fs)
290  {
291  	Tfile *f;
292  
293  	fs->fsize += 8;
294  	f = malloc(fs->fsize*sizeof(*f));
295  	if(f == nil)
296  		error(Enomem);
297  
298  	if(fs->f){
299  		memmove(f, fs->f, fs->nf*sizeof(*f));
300  		free(fs->f);
301  	}
302  	fs->f = f;
303  }
304  
305  static Tfile*
306  newfile(Tfs *fs, char *name)
307  {
308  	int i;
309  	volatile struct {
310  		Tfile *f;
311  		Tfs *fs;
312  	} rock;
313  
314  	/* find free entry in file table */
315  	rock.f = 0;
316  	rock.fs = fs;
317  	for(;;) {
318  		for(i = 0; i < rock.fs->fsize; i++){
319  			rock.f = &rock.fs->f[i];
320  			if(rock.f->name[0] == 0){
321  				strncpy(rock.f->name, name, sizeof(rock.f->name)-1);
322  				break;
323  			}
324  		}
325  
326  		if(i < rock.fs->fsize){
327  			if(i >= rock.fs->nf)
328  				rock.fs->nf = i+1;
329  			break;
330  		}
331  
332  		expand(rock.fs);
333  	}
334  
335  	rock.f->flag = Fcreating;
336  	rock.f->dbno = Notabno;
337  	rock.f->bno = mapalloc(rock.fs);
338  	rock.f->fbno = Notabno;
339  	rock.f->r = 1;
340  	rock.f->pin = up->env->pgrp->pin;
341  
342  	/* write directory block */
343  	if(waserror()){
344  		freefile(rock.fs, rock.f, Notabno);
345  		nexterror();
346  	}
347  	if(rock.f->bno == Notabno)
348  		error("out of space");
349  	writedir(rock.fs, rock.f);
350  	poperror();
351  
352  	return rock.f;
353  }
354  
355  /*
356   *  Read the whole medium and build a file table and used
357   *  block bitmap.  Inconsistent files are purged.  The medium
358   *  had better be small or this could take a while.
359   */
360  static void
361  tfsinit(Tfs *fs)
362  {
363  	uchar dbuf[STATFIXLEN+4*KNAMELEN];
364  	Dir d;
365  	uchar buf[Blen];
366  	ulong x, bno;
367  	int n, done;
368  	Tfile *f;
369  	Mdir *mdir;
370  	Mdata *mdata;
371  
372  	n = devtab[fs->c->type]->stat(fs->c, dbuf, sizeof(dbuf));
373  	if(n == 0)
374  		error(Eshortstat);
375  	n = convM2D(dbuf, n, &d, nil);
376  	if(n == 0)
377  		error(Eshortstat);
378  	fs->nblocks = d.length/Blen;
379  	if(fs->nblocks < 3)
380  		error("tinyfs medium too small");
381  
382  	/* bitmap for block usage */
383  	x = (fs->nblocks + 8 - 1)/8;
384  	fs->map = malloc(x);
385  	if(fs->map == nil)
386  		error(Enomem);
387  	memset(fs->map, 0x0, x);
388  	for(bno = fs->nblocks; bno < x*8; bno++)
389  		mapset(fs, bno);
390  
391  	/* find files */
392  	for(bno = 0; bno < fs->nblocks; bno++){
393  		n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno);
394  		if(n != Blen)
395  			break;
396  
397  		mdir = validdir(fs, buf);
398  		if(mdir == 0)
399  			continue;
400  
401  		if(fs->nf >= fs->fsize)
402  			expand(fs);
403  
404  		f = &fs->f[fs->nf++];
405  
406  		x = GETS(mdir->bno);
407  		mapset(fs, bno);
408  		strncpy(f->name, mdir->name, sizeof(f->name));
409  		f->pin = GETS(mdir->pin);
410  		f->bno = bno;
411  		f->dbno = x;
412  		f->fbno = Notabno;
413  	}
414  
415  	/* follow files */
416  	for(f = fs->f; f < &(fs->f[fs->nf]); f++){
417  		bno = f->dbno;
418  		for(done = 0; !done;) {
419  			if(isalloced(fs, bno)){
420  				freefile(fs, f, bno);
421  				break;
422  			}
423  			n = devtab[fs->c->type]->read(fs->c, buf, Blen, Blen*bno);
424  			if(n != Blen){
425  				freefile(fs, f, bno);
426  				break;
427  			}
428  			mdata = validdata(fs, buf, 0);
429  			if(mdata == 0){
430  				freefile(fs, f, bno);
431  				break;
432  			}
433  			mapset(fs, bno);
434  			switch(mdata->type){
435  			case Tagdata:
436  				bno = GETS(mdata->bno);
437  				f->length += Dlen;
438  				break;
439  			case Tagend:
440  				f->length += GETS(mdata->bno);
441  				done = 1;
442  				break;
443  			}
444  			if(done)
445  				f->flag &= ~Fcreating;
446  		}
447  	}
448  }
449  
450  /*
451   *  single directory
452   */
453  static int
454  tinyfsgen(Chan *c, char*, Dirtab *tab, int ntab, int i, Dir *dp)
455  {
456  	Tfs *fs;
457  	Tfile *f;
458  	Qid qid;
459  
460  	USED(ntab);
461  	USED(tab);
462  
463  	fs = &tinyfs.fs[c->dev];
464  	if(i >= fs->nf)
465  		return -1;
466  	if(i == DEVDOTDOT){
467  		mkqid(&qid, Qdir, 0, QTDIR);
468  		devdir(c, qid, ".", 0, eve, 0555, dp);
469  		return 1;
470  	}
471  	f = &fs->f[i];
472  	if(f->name[0] == 0)
473  		return 0;
474  	mkqid(&qid, i, 0, QTFILE);
475  	devdir(c, qid, f->name, f->length, eve, 0664, dp);
476  	return 1;
477  }
478  
479  static void
480  tinyfsinit(void)
481  {
482  	if(Nlen > KNAMELEN)
483  		panic("tinyfsinit");
484  }
485  
486  /*
487   *  specifier is the name of a device in /dev
488   */
489  static Chan*
490  tinyfsattach(char *spec)
491  {
492  	Tfs *fs;
493  	Chan *c;
494  	volatile struct { Chan *cc; } rock;
495  	int i;
496  	char buf[KNAMELEN*3];
497  
498  	if(*spec == 0)
499  		error("bad specifier");
500  
501  	snprint(buf, sizeof(buf), "/dev/%s", spec);
502  	rock.cc = namec(buf, Aopen, ORDWR, 0);
503  	if(waserror()){
504  		cclose(rock.cc);
505  		nexterror();
506  	}
507  
508  	fs = 0;
509  	for(i = 0; i < Maxfs; i++){
510  		fs = &tinyfs.fs[i];
511  		qlock(&fs->ql);
512  		if(fs->r && eqchan(rock.cc, fs->c, 1))
513  			break;
514  		qunlock(&fs->ql);
515  	}
516  	if(i < Maxfs){
517  		fs->r++;
518  		qunlock(&fs->ql);
519  		cclose(rock.cc);
520  	} else {
521  		for(fs = tinyfs.fs; fs < &tinyfs.fs[Maxfs]; fs++){
522  			qlock(&fs->ql);
523  			if(fs->r == 0)
524  				break;
525  			qunlock(&fs->ql);
526  		}
527  		if(fs == &tinyfs.fs[Maxfs])
528  			error("too many tinyfs's");
529  		fs->c = rock.cc;
530  		fs->r = 1;
531  		fs->f = 0;
532  		fs->nf = 0;
533  		fs->fsize = 0;
534  		tfsinit(fs);
535  		qunlock(&fs->ql);
536  	}
537  	poperror();
538  
539  	c = devattach('F', spec);
540  	c->dev = fs - tinyfs.fs;
541  	c->qid.path = Qdir;
542  	c->qid.type = QTDIR;
543  	c->qid.vers = 0;
544  
545  	return c;
546  }
547  
548  static Walkqid*
549  tinyfswalk(Chan *c, Chan *nc, char **name, int nname)
550  {
551  	Tfs *fs;
552  	Walkqid *w;
553  
554  	fs = &tinyfs.fs[c->dev];
555  
556  	qlock(&fs->ql);
557  	if(waserror()){
558  		qunlock(&fs->ql);
559  		nexterror();
560  	}
561  	w = devwalk(c, nc, name, nname, 0, 0, tinyfsgen);
562  	if(w != nil && w->clone!=nil && w->clone->qid.type != QTDIR){
563  		fs = &tinyfs.fs[w->clone->dev];
564  		fs->r++;
565  		fs->f[(ulong)w->clone->qid.path].r++;
566  	}
567  	poperror();
568  	qunlock(&fs->ql);
569  	return w;
570  }
571  
572  static int
573  tinyfsstat(Chan *c, uchar *db, int n)
574  {
575  	return devstat(c, db, n, 0, 0, tinyfsgen);
576  }
577  
578  static Chan*
579  tinyfsopen(Chan *c, int omode)
580  {
581  	Tfile *f;
582  	volatile struct { Tfs *fs; } rock;
583  
584  	rock.fs = &tinyfs.fs[c->dev];
585  
586  	if(c->qid.type & QTDIR){
587  		if(omode != OREAD)
588  			error(Eperm);
589  	} else {
590  		qlock(&rock.fs->ql);
591  		if(waserror()){
592  			qunlock(&rock.fs->ql);
593  			nexterror();
594  		}
595  		switch(omode){
596  		case OTRUNC|ORDWR:
597  		case OTRUNC|OWRITE:
598  			f = newfile(rock.fs, rock.fs->f[c->qid.path].name);
599  			rock.fs->f[c->qid.path].r--;
600  			c->qid.path = f - rock.fs->f;
601  			break;
602  		case OREAD:
603  			break;
604  		default:
605  			error(Eperm);
606  		}
607  		qunlock(&rock.fs->ql);
608  		poperror();
609  	}
610  
611  	return devopen(c, omode, 0, 0, tinyfsgen);
612  }
613  
614  static void
615  tinyfscreate(Chan *c, char *name, int omode, ulong perm)
616  {
617  	volatile struct { Tfs *fs; } rock;
618  	Tfile *f;
619  
620  	USED(perm);
621  
622  	rock.fs = &tinyfs.fs[c->dev];
623  
624  	qlock(&rock.fs->ql);
625  	if(waserror()){
626  		qunlock(&rock.fs->ql);
627  		nexterror();
628  	}
629  	f = newfile(rock.fs, name);
630  	qunlock(&rock.fs->ql);
631  	poperror();
632  
633  	c->qid.path = f - rock.fs->f;
634  	c->qid.vers = 0;
635  	c->mode = openmode(omode);
636  }
637  
638  static void
639  tinyfsremove(Chan *c)
640  {
641  	Tfs *fs;
642  	Tfile *f;
643  
644  	if(c->qid.type & QTDIR)
645  		error(Eperm);
646  	fs = &tinyfs.fs[c->dev];
647  	f = &fs->f[c->qid.path];
648  	qlock(&fs->ql);
649  	freefile(fs, f, Notabno);
650  	qunlock(&fs->ql);
651  }
652  
653  static void
654  tinyfsclose(Chan *c)
655  {
656  	volatile struct { Tfs *fs; } rock;
657  	Tfile *f, *nf;
658  	int i;
659  
660  	rock.fs = &tinyfs.fs[c->dev];
661  
662  	qlock(&rock.fs->ql);
663  
664  	/* dereference file and remove old versions */
665  	if(!waserror()){
666  		if(c->qid.path != Qdir){
667  			f = &rock.fs->f[c->qid.path];
668  			f->r--;
669  			if(f->r == 0){
670  				if(f->flag & Frmonclose)
671  					freefile(rock.fs, f, Notabno);
672  				else if(f->flag & Fcreating){
673  					/* remove all other files with this name */
674  					for(i = 0; i < rock.fs->fsize; i++){
675  						nf = &rock.fs->f[i];
676  						if(f == nf)
677  							continue;
678  						if(strcmp(nf->name, f->name) == 0){
679  							if(nf->r)
680  								nf->flag |= Frmonclose;
681  							else
682  								freefile(rock.fs, nf, Notabno);
683  						}
684  					}
685  					f->flag &= ~Fcreating;
686  				}
687  			}
688  		}
689  		poperror();
690  	}
691  
692  	/* dereference rock.fs and remove on zero refs */
693  	rock.fs->r--;
694  	if(rock.fs->r == 0){
695  		if(rock.fs->f)
696  			free(rock.fs->f);
697  		rock.fs->f = 0;
698  		rock.fs->nf = 0;
699  		rock.fs->fsize = 0;
700  		if(rock.fs->map)
701  			free(rock.fs->map);
702  		rock.fs->map = 0;
703  		cclose(rock.fs->c);
704  		rock.fs->c = 0;
705  	}
706  	qunlock(&rock.fs->ql);
707  }
708  
709  static long
710  tinyfsread(Chan *c, void *a, long n, vlong offset)
711  {
712  	volatile struct { Tfs *fs; } rock;
713  	Tfile *f;
714  	int sofar, i, off;
715  	ulong bno;
716  	Mdata *md;
717  	uchar buf[Blen];
718  	uchar *p;
719  
720  	if(c->qid.type & QTDIR)
721  		return devdirread(c, a, n, 0, 0, tinyfsgen);
722  
723  	p = a;
724  	rock.fs = &tinyfs.fs[c->dev];
725  	f = &rock.fs->f[c->qid.path];
726  	if(offset >= f->length)
727  		return 0;
728  
729  	qlock(&rock.fs->ql);
730  	if(waserror()){
731  		qunlock(&rock.fs->ql);
732  		nexterror();
733  	}
734  	if(n + offset >= f->length)
735  		n = f->length - offset;
736  
737  	/* walk to starting data block */
738  	if(0 && f->finger <= offset && f->fbno != Notabno){
739  		sofar = f->finger;
740  		bno = f->fbno;
741  	} else {
742  		sofar = 0;
743  		bno = f->dbno;
744  	}
745  	for(; sofar + Dlen <= offset; sofar += Dlen){
746  		md = readdata(rock.fs, bno, buf, 0);
747  		if(md == 0)
748  			error(Eio);
749  		bno = GETS(md->bno);
750  	}
751  
752  	/* read data */
753  	off = offset%Dlen;
754  	offset -= off;
755  	for(sofar = 0; sofar < n; sofar += i){
756  		md = readdata(rock.fs, bno, buf, &i);
757  		if(md == 0)
758  			error(Eio);
759  
760  		/* update finger for successful read */
761  		f->finger = offset;
762  		f->fbno = bno;
763  		offset += Dlen;
764  
765  		i -= off;
766  		if(i > n - sofar)
767  			i = n - sofar;
768  		memmove(p, md->data+off, i);
769  		p += i;
770  		bno = GETS(md->bno);
771  		off = 0;
772  	}
773  	qunlock(&rock.fs->ql);
774  	poperror();
775  
776  	return sofar;
777  }
778  
779  /*
780   *  if we get a write error in this routine, blocks will
781   *  be lost.  They should be recovered next fsinit.
782   */
783  static long
784  tinyfswrite(Chan *c, void *a, long n, vlong offset)
785  {
786  	Tfile *f;
787  	int last, next, i, finger, off, used;
788  	ulong bno, fbno;
789  	Mdata *md;
790  	uchar buf[Blen];
791  	uchar *p;
792  	volatile struct {
793  		Tfs *fs;
794  		ulong dbno;
795  	} rock;
796  
797  	if(c->qid.type & QTDIR)
798  		error(Eperm);
799  
800  	if(n == 0)
801  		return 0;
802  
803  	p = a;
804  	rock.fs = &tinyfs.fs[c->dev];
805  	f = &rock.fs->f[c->qid.path];
806  
807  	qlock(&rock.fs->ql);
808  	rock.dbno = Notabno;
809  	if(waserror()){
810  		freeblocks(rock.fs, rock.dbno, Notabno);
811  		qunlock(&rock.fs->ql);
812  		nexterror();
813  	}
814  
815  	/* files are append only, anything else is illegal */
816  	if(offset != f->length)
817  		error("append only");
818  
819  	/* write blocks backwards */
820  	p += n;
821  	last = offset + n;
822  	fbno = Notabno;
823  	finger = 0;
824  	off = offset; /* so we have something signed to compare against */
825  	for(next = ((last-1)/Dlen)*Dlen; next >= off; next -= Dlen){
826  		bno = mapalloc(rock.fs);
827  		if(bno == Notabno)
828  			error("out of space");
829  		i = last - next;
830  		p -= i;
831  		if(last == n+offset){
832  			writedata(rock.fs, bno, rock.dbno, p, i, 1);
833  			finger = next;	/* remember for later */
834  			fbno = bno;
835  		} else {
836  			writedata(rock.fs, bno, rock.dbno, p, i, 0);
837  		}
838  		rock.dbno = bno;
839  		last = next;
840  	}
841  
842  	/* walk to last data block */
843  	md = (Mdata*)buf;
844  	if(0 && f->finger < offset && f->fbno != Notabno){
845  		next = f->finger;
846  		bno = f->fbno;
847  	} else {
848  		next = 0;
849  		bno = f->dbno;
850  	}
851  
852  	used = 0;
853  	while(bno != Notabno){
854  		md = readdata(rock.fs, bno, buf, &used);
855  		if(md == 0)
856  			error(Eio);
857  		if(md->type == Tagend){
858  			if(next + Dlen < offset)
859  				panic("devtinyfs1");
860  			break;
861  		}
862  		next += Dlen;
863  		if(next > offset)
864  			panic("devtinyfs1");
865  		bno = GETS(md->bno);
866  	}
867  
868  	/* point to new blocks */
869  	if(offset == 0){
870  		/* first block in a file */
871  		f->dbno = rock.dbno;
872  		writedir(rock.fs, f);
873  	} else {
874  		/* updating a current block */
875  		i = last - offset;
876  		if(i > 0){
877  			p -= i;
878  			memmove(md->data + used, p, i);
879  			used += i;
880  		}
881  		writedata(rock.fs, bno, rock.dbno, md->data, used, last == n+offset);
882  	}
883  	f->length += n;
884  
885  	/* update finger */
886  	if(fbno != Notabno){
887  		f->finger = finger;
888  		f->fbno =  fbno;
889  	}
890  	poperror();
891  	qunlock(&rock.fs->ql);
892  
893  	return n;
894  }
895  
896  Dev tinyfsdevtab = {
897  	'F',
898  	"tinyfs",
899  
900  	devreset,
901  	tinyfsinit,
902  	devshutdown,
903  	tinyfsattach,
904  	tinyfswalk,
905  	tinyfsstat,
906  	tinyfsopen,
907  	tinyfscreate,
908  	tinyfsclose,
909  	tinyfsread,
910  	devbread,
911  	tinyfswrite,
912  	devbwrite,
913  	tinyfsremove,
914  	devwstat,
915  };
916