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