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