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
usage(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
main(int argc,char * argv[])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
cachesetup(int format,char * name,char * partition)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
mountinit(char * server,char * mountpoint)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
io(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
rversion(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
rauth(Mfile * mf)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
rflush(void)364 rflush(void) /* synchronous so easy */
365 {
366 sendreply(0);
367 }
368
369 void
rattach(Mfile * mf)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
rwalk(Mfile * mf)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
ropen(Mfile * mf)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
rcreate(Mfile * mf)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
rclunk(Mfile * mf)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
rremove(Mfile * mf)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
rread(Mfile * mf)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
rwrite(Mfile * mf)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
rstat(Mfile * mf)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
rwstat(Mfile * mf)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
error(char * fmt,...)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
warning(char * s)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
sendreply(char * err)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
delegate(void)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
askserver(void)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
sendmsg(P9fs * p,Fcall * f)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
dump(uchar * p,int len)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
rcvmsg(P9fs * p,Fcall * f)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
ctltest(Mfile * mf)831 ctltest(Mfile *mf)
832 {
833 return mf->busy && mf->qid.type == ctlqid.type &&
834 mf->qid.path == ctlqid.path;
835 }
836
837 void
genstats(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