1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 6 #include "cformat.h" 7 #include "lru.h" 8 #include "bcache.h" 9 #include "disk.h" 10 #include "inode.h" 11 #include "file.h" 12 #include "stats.h" 13 14 enum 15 { 16 Nfid= 10240, 17 }; 18 19 /* maximum length of a file */ 20 #define MAXLEN 0x7fffffffffffffffLL 21 22 typedef struct Mfile Mfile; 23 typedef struct Ram Ram; 24 typedef struct P9fs P9fs; 25 26 struct Mfile 27 { 28 Qid qid; 29 char busy; 30 }; 31 32 Mfile mfile[Nfid]; 33 Icache ic; 34 int debug, statson; 35 36 struct P9fs 37 { 38 int fd[2]; 39 Fcall rhdr; 40 Fcall thdr; 41 long len; 42 char *name; 43 }; 44 45 P9fs c; /* client conversation */ 46 P9fs s; /* server conversation */ 47 48 struct Cfsstat cfsstat, cfsprev; 49 char statbuf[2048]; 50 int statlen; 51 52 #define MAXFDATA 8192 /* i/o size for read/write */ 53 54 int messagesize = MAXFDATA+IOHDRSZ; 55 56 uchar datasnd[MAXFDATA + IOHDRSZ]; 57 uchar datarcv[MAXFDATA + IOHDRSZ]; 58 59 Qid rootqid; 60 Qid ctlqid = {0x5555555555555555LL, 0, 0}; 61 62 void rversion(void); 63 void rauth(Mfile*); 64 void rflush(void); 65 void rattach(Mfile*); 66 void rwalk(Mfile*); 67 void ropen(Mfile*); 68 void rcreate(Mfile*); 69 void rread(Mfile*); 70 void rwrite(Mfile*); 71 void rclunk(Mfile*); 72 void rremove(Mfile*); 73 void rstat(Mfile*); 74 void rwstat(Mfile*); 75 void error(char*, ...); 76 void warning(char*); 77 void mountinit(char*, char*); 78 void io(void); 79 void sendreply(char*); 80 void sendmsg(P9fs*, Fcall*); 81 void rcvmsg(P9fs*, Fcall*); 82 int delegate(void); 83 int askserver(void); 84 void cachesetup(int, char*); 85 int ctltest(Mfile*); 86 void genstats(void); 87 88 char *mname[]={ 89 [Tversion] "Tversion", 90 [Tauth] "Tauth", 91 [Tflush] "Tflush", 92 [Tattach] "Tattach", 93 [Twalk] "Twalk", 94 [Topen] "Topen", 95 [Tcreate] "Tcreate", 96 [Tclunk] "Tclunk", 97 [Tread] "Tread", 98 [Twrite] "Twrite", 99 [Tremove] "Tremove", 100 [Tstat] "Tstat", 101 [Twstat] "Twstat", 102 [Rversion] "Rversion", 103 [Rauth] "Rauth", 104 [Rerror] "Rerror", 105 [Rflush] "Rflush", 106 [Rattach] "Rattach", 107 [Rwalk] "Rwalk", 108 [Ropen] "Ropen", 109 [Rcreate] "Rcreate", 110 [Rclunk] "Rclunk", 111 [Rread] "Rread", 112 [Rwrite] "Rwrite", 113 [Rremove] "Rremove", 114 [Rstat] "Rstat", 115 [Rwstat] "Rwstat", 116 0, 117 }; 118 119 void 120 usage(void) 121 { 122 fprint(2, "usage:\tcfs -s [-rd] [-f partition]"); 123 fprint(2, "\tcfs [-rd] [-f partition] [-a netaddr] [mt-pt]\n"); 124 exits("usage"); 125 } 126 127 void 128 main(int argc, char *argv[]) 129 { 130 int std; 131 int format; 132 char *part; 133 char *server; 134 char *mtpt; 135 136 std = 0; 137 format = 0; 138 part = "/dev/sdC0/cache"; 139 server = "il!emelie"; 140 mtpt = "/tmp"; 141 142 ARGBEGIN{ 143 case 'a': 144 server = ARGF(); 145 if(server == 0) 146 usage(); 147 break; 148 case 'S': 149 statson = 1; 150 break; 151 case 's': 152 std = 1; 153 break; 154 case 'r': 155 format = 1; 156 break; 157 case 'f': 158 part = ARGF(); 159 if(part == 0) 160 usage(); 161 break; 162 case 'd': 163 debug = 1; 164 break; 165 default: 166 usage(); 167 }ARGEND 168 if(argc && *argv) 169 mtpt = *argv; 170 171 if(debug) 172 fmtinstall('F', fcallfmt); 173 174 cachesetup(format, part); 175 176 c.name = "client"; 177 s.name = "server"; 178 if(std){ 179 c.fd[0] = c.fd[1] = 1; 180 s.fd[0] = s.fd[1] = 0; 181 }else 182 mountinit(server, mtpt); 183 184 switch(fork()){ 185 case 0: 186 io(); 187 exits(""); 188 case -1: 189 error("fork"); 190 default: 191 exits(""); 192 } 193 } 194 195 void 196 cachesetup(int format, char *partition) 197 { 198 int f; 199 int secsize; 200 int inodes; 201 int blocksize; 202 203 secsize = 512; 204 inodes = 1024; 205 blocksize = 4*1024; 206 207 f = open(partition, ORDWR); 208 if(f < 0) 209 error("opening partition"); 210 211 if(format || iinit(&ic, f, secsize)<0){ 212 if(iformat(&ic, f, inodes, "bootes", blocksize, secsize) < 0) 213 error("formatting failed"); 214 } 215 } 216 217 void 218 mountinit(char *server, char *mountpoint) 219 { 220 int p[2]; 221 222 /* 223 * grab a channel and call up the file server 224 */ 225 s.fd[0] = s.fd[1] = dial(netmkaddr(server, 0, "9fs"), 0, 0, 0); 226 if(s.fd[0] < 0) 227 error("opening data"); 228 229 /* 230 * mount onto name space 231 */ 232 if(pipe(p) < 0) 233 error("pipe failed"); 234 switch(fork()){ 235 case 0: 236 break; 237 default: 238 if(amount(p[1], mountpoint, MREPL|MCREATE, "") < 0) 239 error("mount failed"); 240 exits(0); 241 case -1: 242 error("fork failed\n"); 243 /*BUG: no wait!*/ 244 } 245 c.fd[0] = c.fd[1] = p[0]; 246 } 247 248 void 249 io(void) 250 { 251 int type; 252 Mfile *mf; 253 loop: 254 rcvmsg(&c, &c.thdr); 255 256 type = c.thdr.type; 257 258 if(statson){ 259 cfsstat.cm[type].n++; 260 cfsstat.cm[type].s = nsec(); 261 } 262 mf = &mfile[c.thdr.fid]; 263 switch(type){ 264 default: 265 error("type"); 266 break; 267 case Tversion: 268 rversion(); 269 break; 270 case Tauth: 271 mf = &mfile[c.thdr.afid]; 272 rauth(mf); 273 break; 274 case Tflush: 275 rflush(); 276 break; 277 case Tattach: 278 rattach(mf); 279 break; 280 case Twalk: 281 rwalk(mf); 282 break; 283 case Topen: 284 ropen(mf); 285 break; 286 case Tcreate: 287 rcreate(mf); 288 break; 289 case Tread: 290 rread(mf); 291 break; 292 case Twrite: 293 rwrite(mf); 294 break; 295 case Tclunk: 296 rclunk(mf); 297 break; 298 case Tremove: 299 rremove(mf); 300 break; 301 case Tstat: 302 rstat(mf); 303 break; 304 case Twstat: 305 rwstat(mf); 306 break; 307 } 308 if(statson){ 309 cfsstat.cm[type].t += nsec() -cfsstat.cm[type].s; 310 } 311 goto loop; 312 } 313 314 void 315 rversion(void) 316 { 317 if(messagesize > c.thdr.msize) 318 messagesize = c.thdr.msize; 319 c.thdr.msize = messagesize; /* set downstream size */ 320 delegate(); 321 } 322 323 void 324 rauth(Mfile *mf) 325 { 326 if(mf->busy) 327 error("auth to used channel"); 328 329 if(delegate() == 0){ 330 mf->qid = s.rhdr.aqid; 331 mf->busy = 1; 332 } 333 } 334 335 void 336 rflush(void) /* synchronous so easy */ 337 { 338 sendreply(0); 339 } 340 341 void 342 rattach(Mfile *mf) 343 { 344 if(delegate() == 0){ 345 mf->qid = s.rhdr.qid; 346 mf->busy = 1; 347 if (statson == 1){ 348 statson++; 349 rootqid = mf->qid; 350 } 351 } 352 } 353 354 void 355 rwalk(Mfile *mf) 356 { 357 Mfile *nmf; 358 359 nmf = nil; 360 if(statson 361 && mf->qid.type == rootqid.type && mf->qid.path == rootqid.path 362 && c.thdr.nwname == 1 && strcmp(c.thdr.wname[0], "cfsctl") == 0){ 363 /* This is the ctl file */ 364 nmf = &mfile[c.thdr.newfid]; 365 if(c.thdr.newfid != c.thdr.fid && nmf->busy) 366 error("clone to used channel"); 367 nmf = &mfile[c.thdr.newfid]; 368 nmf->qid = ctlqid; 369 nmf->busy = 1; 370 c.rhdr.nwqid = 1; 371 c.rhdr.wqid[0] = ctlqid; 372 sendreply(0); 373 return; 374 } 375 if(c.thdr.newfid != c.thdr.fid){ 376 if(c.thdr.newfid<0 || Nfid<=c.thdr.newfid) 377 error("clone nfid out of range"); 378 nmf = &mfile[c.thdr.newfid]; 379 if(nmf->busy) 380 error("clone to used channel"); 381 nmf = &mfile[c.thdr.newfid]; 382 nmf->qid = mf->qid; 383 nmf->busy = 1; 384 mf = nmf; /* Walk mf */ 385 } 386 387 if(delegate() < 0){ /* complete failure */ 388 if(nmf) 389 nmf->busy = 0; 390 return; 391 } 392 393 if(s.rhdr.nwqid == c.thdr.nwname){ /* complete success */ 394 if(s.rhdr.nwqid > 0) 395 mf->qid = s.rhdr.wqid[s.rhdr.nwqid-1]; 396 return; 397 } 398 399 /* partial success; release fid */ 400 if(nmf) 401 nmf->busy = 0; 402 } 403 404 void 405 ropen(Mfile *mf) 406 { 407 if(statson && ctltest(mf)){ 408 /* Opening ctl file */ 409 if(c.thdr.mode != OREAD){ 410 sendreply("does not exist"); 411 return; 412 } 413 c.rhdr.qid = ctlqid; 414 c.rhdr.iounit = 0; 415 sendreply(0); 416 genstats(); 417 return; 418 } 419 if(delegate() == 0){ 420 mf->qid = s.rhdr.qid; 421 if(c.thdr.mode & OTRUNC) 422 iget(&ic, mf->qid); 423 } 424 } 425 426 void 427 rcreate(Mfile *mf) 428 { 429 if(statson && ctltest(mf)){ 430 sendreply("exists"); 431 return; 432 } 433 if(delegate() == 0){ 434 mf->qid = s.rhdr.qid; 435 mf->qid.vers++; 436 } 437 } 438 439 void 440 rclunk(Mfile *mf) 441 { 442 if(!mf->busy){ 443 sendreply(0); 444 return; 445 } 446 mf->busy = 0; 447 delegate(); 448 } 449 450 void 451 rremove(Mfile *mf) 452 { 453 if(statson && ctltest(mf)){ 454 sendreply("not removed"); 455 return; 456 } 457 mf->busy = 0; 458 delegate(); 459 } 460 461 void 462 rread(Mfile *mf) 463 { 464 int cnt; 465 long off, first; 466 char *cp; 467 Ibuf *b; 468 long n; 469 char data[MAXFDATA]; 470 int done; 471 472 first = off = c.thdr.offset; 473 cnt = c.thdr.count; 474 475 if(statson && ctltest(mf)){ 476 if(cnt > statlen-off) 477 c.rhdr.count = statlen-off; 478 else 479 c.rhdr.count = cnt; 480 if(c.rhdr.count < 0){ 481 sendreply("eof"); 482 return; 483 } 484 c.rhdr.data = statbuf + off; 485 sendreply(0); 486 return; 487 } 488 if(mf->qid.type & (QTDIR|QTAUTH)){ 489 delegate(); 490 if (statson) { 491 cfsstat.ndirread++; 492 if(c.rhdr.count > 0){ 493 cfsstat.bytesread += c.rhdr.count; 494 cfsstat.bytesfromdirs += c.rhdr.count; 495 } 496 } 497 return; 498 } 499 500 b = iget(&ic, mf->qid); 501 if(b == 0){ 502 DPRINT(2, "delegating read\n"); 503 delegate(); 504 if (statson){ 505 cfsstat.ndelegateread++; 506 if(c.rhdr.count > 0){ 507 cfsstat.bytesread += c.rhdr.count; 508 cfsstat.bytesfromserver += c.rhdr.count; 509 } 510 } 511 return; 512 } 513 514 cp = data; 515 done = 0; 516 while(cnt>0 && !done){ 517 if(off >= b->inode.length){ 518 DPRINT(2, "offset %ld greater than length %lld\n", off, b->inode.length); 519 break; 520 } 521 n = fread(&ic, b, cp, off, cnt); 522 if(n <= 0){ 523 n = -n; 524 if(n==0 || n>cnt) 525 n = cnt; 526 DPRINT(2, "fetch %ld bytes of data from server at offset %ld\n", n, off); 527 s.thdr.type = c.thdr.type; 528 s.thdr.fid = c.thdr.fid; 529 s.thdr.tag = c.thdr.tag; 530 s.thdr.offset = off; 531 s.thdr.count = n; 532 if(statson){ 533 cfsstat.ndelegateread++; 534 } 535 if(askserver() < 0){ 536 sendreply(s.rhdr.ename); 537 return; 538 } 539 if(s.rhdr.count != n) 540 done = 1; 541 n = s.rhdr.count; 542 if(n == 0){ 543 /* end of file */ 544 if(b->inode.length > off){ 545 DPRINT(2, "file %llud.%ld, length %ld\n", 546 b->inode.qid.path, b->inode.qid.vers, off); 547 b->inode.length = off; 548 } 549 break; 550 } 551 memmove(cp, s.rhdr.data, n); 552 fwrite(&ic, b, cp, off, n); 553 if (statson){ 554 cfsstat.bytestocache += n; 555 cfsstat.bytesfromserver += n; 556 } 557 }else{ 558 DPRINT(2, "fetched %ld bytes from cache\n", n); 559 if(statson){ 560 cfsstat.bytesfromcache += n; 561 } 562 } 563 cnt -= n; 564 off += n; 565 cp += n; 566 } 567 c.rhdr.data = data; 568 c.rhdr.count = off - first; 569 if(statson){ 570 cfsstat.bytesread += c.rhdr.count; 571 } 572 sendreply(0); 573 } 574 575 void 576 rwrite(Mfile *mf) 577 { 578 Ibuf *b; 579 char buf[MAXFDATA]; 580 581 if(statson && ctltest(mf)){ 582 sendreply("read only"); 583 return; 584 } 585 if(mf->qid.type & (QTDIR|QTAUTH)){ 586 delegate(); 587 if(statson && c.rhdr.count > 0) 588 cfsstat.byteswritten += c.rhdr.count; 589 return; 590 } 591 592 memmove(buf, c.thdr.data, c.thdr.count); 593 if(delegate() < 0) 594 return; 595 596 if(s.rhdr.count > 0) 597 cfsstat.byteswritten += s.rhdr.count; 598 if(mf->qid.type & QTAPPEND) /* don't modify our cache for append-only data; always read from server*/ 599 return; 600 b = iget(&ic, mf->qid); 601 if(b == 0) 602 return; 603 if (b->inode.length < c.thdr.offset + s.rhdr.count) 604 b->inode.length = c.thdr.offset + s.rhdr.count; 605 mf->qid.vers++; 606 if (s.rhdr.count != c.thdr.count) 607 syslog(0, "cfslog", "rhdr.count %ud, thdr.count %ud\n", 608 s.rhdr.count, c.thdr.count); 609 if(fwrite(&ic, b, buf, c.thdr.offset, s.rhdr.count) == s.rhdr.count){ 610 iinc(&ic, b); 611 if(statson) 612 cfsstat.bytestocache += s.rhdr.count; 613 } 614 } 615 616 void 617 rstat(Mfile *mf) 618 { 619 Dir d; 620 621 if(statson && ctltest(mf)){ 622 genstats(); 623 d.qid = ctlqid; 624 d.mode = 0444; 625 d.length = statlen; /* would be nice to do better */ 626 d.name = "cfsctl"; 627 d.uid = "none"; 628 d.gid = "none"; 629 d.muid = "none"; 630 d.atime = time(nil); 631 d.mtime = d.atime; 632 c.rhdr.nstat = convD2M(&d, c.rhdr.stat, sizeof(c.rhdr) - (c.rhdr.stat - (uchar*)&c.rhdr)); 633 sendreply(0); 634 return; 635 } 636 if(delegate() == 0){ 637 Ibuf *b; 638 639 convM2D(s.rhdr.stat, s.rhdr.nstat , &d, nil); 640 mf->qid = d.qid; 641 b = iget(&ic, mf->qid); 642 if(b) 643 b->inode.length = d.length; 644 } 645 } 646 647 void 648 rwstat(Mfile *mf) 649 { 650 Ibuf *b; 651 652 if(statson && ctltest(mf)){ 653 sendreply("read only"); 654 return; 655 } 656 delegate(); 657 if(b = iget(&ic, mf->qid)) 658 b->inode.length = MAXLEN; 659 } 660 661 void 662 error(char *fmt, ...) { 663 va_list arg; 664 static char buf[2048]; 665 666 va_start(arg, fmt); 667 vseprint(buf, buf+sizeof(buf), fmt, arg); 668 va_end(arg); 669 fprint(2, "%s: %s\n", argv0, buf); 670 exits("error"); 671 } 672 673 void 674 warning(char *s) 675 { 676 fprint(2, "cfs: %s: %r\n", s); 677 } 678 679 /* 680 * send a reply to the client 681 */ 682 void 683 sendreply(char *err) 684 { 685 686 if(err){ 687 c.rhdr.type = Rerror; 688 c.rhdr.ename = err; 689 }else{ 690 c.rhdr.type = c.thdr.type+1; 691 c.rhdr.fid = c.thdr.fid; 692 } 693 c.rhdr.tag = c.thdr.tag; 694 sendmsg(&c, &c.rhdr); 695 } 696 697 /* 698 * send a request to the server, get the reply, and send that to 699 * the client 700 */ 701 int 702 delegate(void) 703 { 704 int type; 705 706 type = c.thdr.type; 707 if(statson){ 708 cfsstat.sm[type].n++; 709 cfsstat.sm[type].s = nsec(); 710 } 711 712 sendmsg(&s, &c.thdr); 713 rcvmsg(&s, &s.rhdr); 714 715 if(statson){ 716 cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s; 717 } 718 719 sendmsg(&c, &s.rhdr); 720 return c.thdr.type+1 == s.rhdr.type ? 0 : -1; 721 } 722 723 /* 724 * send a request to the server and get a reply 725 */ 726 int 727 askserver(void) 728 { 729 int type; 730 731 s.thdr.tag = c.thdr.tag; 732 733 type = s.thdr.type; 734 if(statson){ 735 cfsstat.sm[type].n++; 736 cfsstat.sm[type].s = nsec(); 737 } 738 739 sendmsg(&s, &s.thdr); 740 rcvmsg(&s, &s.rhdr); 741 742 if(statson){ 743 cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s; 744 } 745 746 return s.thdr.type+1 == s.rhdr.type ? 0 : -1; 747 } 748 749 /* 750 * send/receive messages with logging 751 */ 752 void 753 sendmsg(P9fs *p, Fcall *f) 754 { 755 DPRINT(2, "->%s: %F\n", p->name, f); 756 757 p->len = convS2M(f, datasnd, messagesize); 758 if(p->len <= 0) 759 error("convS2M"); 760 if(write(p->fd[1], datasnd, p->len)!=p->len) 761 error("sendmsg"); 762 } 763 764 void 765 dump(uchar *p, int len) 766 { 767 fprint(2, "%d bytes", len); 768 while(len > 0){ 769 fprint(2, " %.2ux", *p++); 770 len--; 771 } 772 fprint(2, "\n"); 773 } 774 775 void 776 rcvmsg(P9fs *p, Fcall *f) 777 { 778 int olen, rlen; 779 char buf[128]; 780 781 olen = p->len; 782 p->len = read9pmsg(p->fd[0], datarcv, sizeof(datarcv)); 783 if(p->len <= 0){ 784 sprint(buf, "read9pmsg(%d)->%ld: %r", p->fd[0], p->len); 785 error(buf); 786 } 787 788 if((rlen = convM2S(datarcv, p->len, f)) != p->len) 789 error("rcvmsg format error, expected length %d, got %d", rlen, p->len); 790 if(f->fid<0 || Nfid<=f->fid){ 791 fprint(2, "<-%s: %d %s on %d\n", p->name, f->type, 792 mname[f->type]? mname[f->type] : "mystery", 793 f->fid); 794 dump((uchar*)datasnd, olen); 795 dump((uchar*)datarcv, p->len); 796 error("rcvmsg fid out of range"); 797 } 798 DPRINT(2, "<-%s: %F\n", p->name, f); 799 } 800 801 int 802 ctltest(Mfile *mf) 803 { 804 return mf->busy && mf->qid.type == ctlqid.type && mf->qid.path == ctlqid.path; 805 } 806 807 void 808 genstats(void) 809 { 810 int i; 811 char *p; 812 813 p = statbuf; 814 815 p += snprint(p, sizeof(statbuf)+statbuf-p, " Client Server\n"); 816 p += snprint(p, sizeof(statbuf)+statbuf-p, " #calls Δ ms/call Δ #calls Δ ms/call Δ\n"); 817 for (i = 0; i < nelem(cfsstat.cm); i++) 818 if(cfsstat.cm[i].n || cfsstat.sm[i].n) { 819 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ", 820 cfsstat.cm[i].n, cfsstat.cm[i].n - cfsprev.cm[i].n); 821 if (cfsstat.cm[i].n) 822 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 823 0.000001*cfsstat.cm[i].t/cfsstat.cm[i].n); 824 else 825 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 826 if(cfsstat.cm[i].n - cfsprev.cm[i].n) 827 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 828 0.000001*(cfsstat.cm[i].t - cfsprev.cm[i].t)/(cfsstat.cm[i].n - cfsprev.cm[i].n)); 829 else 830 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 831 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ", 832 cfsstat.sm[i].n, cfsstat.sm[i].n - cfsprev.sm[i].n); 833 if (cfsstat.sm[i].n) 834 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 835 0.000001*cfsstat.sm[i].t/cfsstat.sm[i].n); 836 else 837 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 838 if(cfsstat.sm[i].n - cfsprev.sm[i].n) 839 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 840 0.000001*(cfsstat.sm[i].t - cfsprev.sm[i].t)/(cfsstat.sm[i].n - cfsprev.sm[i].n)); 841 else 842 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 843 p += snprint(p, sizeof(statbuf)+statbuf-p, "%s\n", mname[i]); 844 } 845 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndirread\n", 846 cfsstat.ndirread, cfsstat.ndirread - cfsprev.ndirread); 847 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndelegateread\n", 848 cfsstat.ndelegateread, cfsstat.ndelegateread - cfsprev.ndelegateread); 849 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ninsert\n", 850 cfsstat.ninsert, cfsstat.ninsert - cfsprev.ninsert); 851 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndelete\n", 852 cfsstat.ndelete, cfsstat.ndelete - cfsprev.ndelete); 853 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud nupdate\n", 854 cfsstat.nupdate, cfsstat.nupdate - cfsprev.nupdate); 855 856 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesread\n", 857 cfsstat.bytesread, cfsstat.bytesread - cfsprev.bytesread); 858 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud byteswritten\n", 859 cfsstat.byteswritten, cfsstat.byteswritten - cfsprev.byteswritten); 860 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromserver\n", 861 cfsstat.bytesfromserver, cfsstat.bytesfromserver - cfsprev.bytesfromserver); 862 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromdirs\n", 863 cfsstat.bytesfromdirs, cfsstat.bytesfromdirs - cfsprev.bytesfromdirs); 864 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromcache\n", 865 cfsstat.bytesfromcache, cfsstat.bytesfromcache - cfsprev.bytesfromcache); 866 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytestocache\n", 867 cfsstat.bytestocache, cfsstat.bytestocache - cfsprev.bytestocache); 868 statlen = p - statbuf; 869 cfsprev = cfsstat; 870 } 871