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
checksum(uchar * p)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
mapclr(Tfs * fs,ulong bno)103 mapclr(Tfs *fs, ulong bno)
104 {
105 fs->map[bno>>3] &= ~(1<<(bno&7));
106 }
107
108 static void
mapset(Tfs * fs,ulong bno)109 mapset(Tfs *fs, ulong bno)
110 {
111 fs->map[bno>>3] |= 1<<(bno&7);
112 }
113
114 static int
isalloced(Tfs * fs,ulong bno)115 isalloced(Tfs *fs, ulong bno)
116 {
117 return fs->map[bno>>3] & (1<<(bno&7));
118 }
119
120 static int
mapalloc(Tfs * fs)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*
validdir(Tfs * fs,uchar * p)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*
validdata(Tfs * fs,uchar * p,int * lenp)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*
readdata(Tfs * fs,ulong bno,uchar * buf,int * lenp)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
writedata(Tfs * fs,ulong bno,ulong next,uchar * buf,int len,int last)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
writedir(Tfs * fs,Tfile * f)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
freeblocks(Tfs * fs,ulong bno,ulong bend)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
freefile(Tfs * fs,Tfile * f,ulong bend)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
expand(Tfs * fs)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*
newfile(Tfs * fs,char * name)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 = Notapin;
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
tfsinit(Tfs * fs)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
tinyfsgen(Chan * c,char *,Dirtab * tab,int ntab,int i,Dir * dp)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
tinyfsinit(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*
tinyfsattach(char * spec)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*
tinyfswalk(Chan * c,Chan * nc,char ** name,int nname)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
tinyfsstat(Chan * c,uchar * db,int n)573 tinyfsstat(Chan *c, uchar *db, int n)
574 {
575 return devstat(c, db, n, 0, 0, tinyfsgen);
576 }
577
578 static Chan*
tinyfsopen(Chan * c,int omode)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
tinyfscreate(Chan * c,char * name,int omode,ulong perm)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
tinyfsremove(Chan * c)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
tinyfsclose(Chan * c)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
tinyfsread(Chan * c,void * a,long n,vlong offset)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
tinyfswrite(Chan * c,void * a,long n,vlong offset)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