xref: /inferno-os/os/port/devtinyfs.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
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