1 #include <u.h>
2 #include <libc.h>
3 #include <auth.h>
4 #include <fcall.h>
5 #include <thread.h>
6
7 /*
8 * to do:
9 * - concurrency?
10 * - only worthwhile with several connections, perhaps to several file servers
11 * - cache directories
12 * - message size (dynamic buffers)
13 * - more useful stats
14 * - notes
15 * - lru for sfids in paths (also correct ref counts for paths)
16 */
17
18 #include "dat.h"
19 #include "fns.h"
20 #include "stats.h"
21
22 #define DPRINT if(debug)fprint
23
24 /* maximum length of a file (used for unknown lengths) */
25 enum { MAXLEN = ((uvlong)1<<63)-1 };
26
27 int debug, statson, noauth, openserver;
28
29 #define MAXFDATA 8192 /* i/o size for read/write */
30
31 struct P9fs
32 {
33 int fd[2];
34 Fcall thdr;
35 Fcall rhdr;
36 uchar sndbuf[MAXFDATA+IOHDRSZ]; /* TO DO: dynamic */
37 uchar rcvbuf[2][MAXFDATA + IOHDRSZ]; /* extra buffer to protect client data over server calls */
38 int cb; /* current buffer */
39 long len;
40 char *name;
41 };
42
43 P9fs c; /* client conversation */
44 P9fs s; /* server conversation */
45
46 Host host[1]; /* clients, just the one for now */
47
48 Cfsstat cfsstat, cfsprev;
49 char statbuf[2048];
50 int statlen;
51
52 int messagesize = MAXFDATA+IOHDRSZ; /* must be the same for all connections */
53
54 Qid rootqid;
55 Qid ctlqid = {0x5555555555555555LL, 0, 0};
56
57 uint cachesize = 16 * 1024 * 1024;
58
59 /*
60 * clients directly access directories, auth files, append-only files, exclusive-use files, and mount points
61 */
62 #define isdirect(qid) (((qid).type & ~QTTMP) != QTFILE)
63
64 static char Efidinuse[] = "fid in use";
65 static char Ebadfid[] = "invalid fid";
66 static char Enotdir[] = "not a directory";
67 static char Enonexist[] = "file does not exist";
68 static char Eexist[] = "file already exists";
69 static char Eopened[] = "opened for I/O";
70 static char Emode[] = "bad open mode";
71 static char Eremoved[] = "file has been removed";
72
73 static SFid* freefids;
74 static u32int fidgen;
75
76 static Data datalist;
77
78 static Auth* authlist;
79
80 void
usage(void)81 usage(void)
82 {
83 fprint(2, "usage: %s [-a netaddr | -F srv] [-dknS] [mntpt] [spec]\n", argv0);
84 threadexitsall("usage");
85 }
86
87 void
threadmain(int argc,char * argv[])88 threadmain(int argc, char *argv[])
89 {
90 int std;
91 char *server, *mtpt, *aname;
92
93 std = 0;
94 server = "net!$server";
95 mtpt = "/n/remote";
96 aname = "";
97
98 ARGBEGIN{
99 case 'a':
100 server = EARGF(usage());
101 break;
102 case 'd':
103 debug = 1;
104 break;
105 case 'F':
106 server = EARGF(usage());
107 openserver = 1;
108 break;
109 case 'm':
110 cachesize = atoi(EARGF(usage())) * 1024 * 1024;
111 if(cachesize < 8 * 1024 * 1024 ||
112 cachesize > 3750UL * 1024 * 1024)
113 sysfatal("implausible cache size %ud", cachesize);
114 break;
115 case 'n':
116 noauth = 1;
117 break;
118 case 'S':
119 statson = 1;
120 break;
121 case 's':
122 std = 1;
123 break;
124 default:
125 usage();
126 }ARGEND
127 if(argc && *argv)
128 mtpt = *argv;
129 if(argc > 1 && argv[1])
130 aname = argv[1];
131
132 quotefmtinstall();
133 if(debug){
134 fmtinstall('F', fcallfmt);
135 fmtinstall('D', dirfmt);
136 fmtinstall('M', dirmodefmt);
137 }
138
139 c.name = "client";
140 s.name = "server";
141 if(std){
142 c.fd[0] = c.fd[1] = 1;
143 s.fd[0] = s.fd[1] = 0;
144 }else
145 mountinit(server, mtpt, aname);
146
147 switch(fork()){
148 case 0:
149 io();
150 threadexits("");
151 case -1:
152 error("fork");
153 default:
154 _exits("");
155 }
156 }
157
158 /*
159 * TO DO: need to use libthread variants
160 */
161 void
mountinit(char * server,char * mountpoint,char * aname)162 mountinit(char *server, char *mountpoint, char *aname)
163 {
164 int err;
165 int p[2];
166
167 /*
168 * grab a channel and call up the file server
169 */
170 if(openserver){
171 s.fd[0] = open(server, ORDWR);
172 if(s.fd[0] < 0)
173 error("opening srv file %s: %r", server);
174 } else {
175 s.fd[0] = dial(netmkaddr(server, 0, "9fs"), 0, 0, 0);
176 if(s.fd[0] < 0)
177 error("dialing %s: %r", server);
178 }
179 s.fd[1] = s.fd[0];
180
181 /*
182 * mount onto name space
183 */
184 if(pipe(p) < 0)
185 error("pipe failed");
186 switch(fork()){
187 case 0:
188 break;
189 default:
190 rfork(RFFDG);
191 close(p[0]);
192 if(noauth)
193 err = mount(p[1], -1, mountpoint, MREPL|MCREATE|MCACHE, aname);
194 else
195 err = amount(p[1], mountpoint, MREPL|MCREATE|MCACHE, aname);
196 if(err < 0)
197 error("mount failed: %r");
198 _exits(0);
199 case -1:
200 error("fork failed\n");
201 /*BUG: no wait!*/
202 }
203 close(p[1]);
204 c.fd[0] = c.fd[1] = p[0];
205 }
206
207 void
io(void)208 io(void)
209 {
210 Fcall *t;
211
212 datainit();
213 t = &c.thdr;
214
215 loop:
216 rcvmsg(&c, t);
217
218 if(statson){
219 cfsstat.cm[t->type].n++;
220 cfsstat.cm[t->type].s = nsec();
221 }
222 switch(t->type){
223 default:
224 sendreply("invalid 9p operation");
225 break;
226 case Tversion:
227 rversion(t);
228 break;
229 case Tauth:
230 rauth(t);
231 break;
232 case Tflush:
233 rflush(t);
234 break;
235 case Tattach:
236 rattach(t);
237 break;
238 case Twalk:
239 rwalk(t);
240 break;
241 case Topen:
242 ropen(t);
243 break;
244 case Tcreate:
245 rcreate(t);
246 break;
247 case Tread:
248 rread(t);
249 break;
250 case Twrite:
251 rwrite(t);
252 break;
253 case Tclunk:
254 rclunk(t);
255 break;
256 case Tremove:
257 rremove(t);
258 break;
259 case Tstat:
260 rstat(t);
261 break;
262 case Twstat:
263 rwstat(t);
264 break;
265 }
266 if(statson){
267 cfsstat.cm[t->type].t += nsec() -cfsstat.cm[t->type].s;
268 }
269 goto loop;
270 }
271
272 void
rversion(Fcall * t)273 rversion(Fcall *t)
274 {
275 if(messagesize > t->msize)
276 messagesize = t->msize;
277 t->msize = messagesize; /* set downstream size */
278 delegate(t, nil, nil);
279 }
280
281 void
rauth(Fcall * t)282 rauth(Fcall *t)
283 {
284 Fid *mf;
285
286 mf = findfid(host, t->afid, 1);
287 if(mf == nil)
288 return;
289 mf->path = newpath(nil, "#auth", (Qid){0, 0, 0}); /* path name is never used */
290 mf->opened = allocsfid(); /* newly allocated */
291 if(delegate(t, mf, nil) == 0){
292 mf->qid = s.rhdr.aqid;
293 mf->mode = ORDWR;
294 }else{
295 freesfid(mf->opened); /* free, don't clunk: failed to establish */
296 mf->opened = nil;
297 putfid(mf);
298 }
299 }
300
301 void
rflush(Fcall *)302 rflush(Fcall*) /* synchronous so easy */
303 {
304 /*TO DO: need a tag hash to find requests, once auth is asynchronous */
305 sendreply(0);
306 }
307
308 void
rattach(Fcall * t)309 rattach(Fcall *t)
310 {
311 Fid *mf, *afid;
312 SFid *sfid;
313
314 mf = findfid(host, t->fid, 1);
315 if(mf == nil)
316 return;
317 sfid = nil;
318 if(t->afid != NOFID){
319 afid = findfid(host, t->afid, 0);
320 if(afid == nil)
321 return;
322 sfid = afid->opened;
323 if((afid->qid.type & QTAUTH) == 0 || sfid == nil){
324 sendreply("bad auth file");
325 return;
326 }
327 }
328 mf->path = newpath(nil, "", (Qid){0, 0, 0});
329 mf->path->sfid = allocsfid(); /* newly allocated */
330 if(delegate(t, mf, sfid) == 0){
331 mf->qid = s.rhdr.qid;
332 mf->path->qid = mf->qid;
333 if(statson == 1){
334 statson++;
335 rootqid = mf->qid;
336 }
337 }else{
338 freesfid(mf->path->sfid); /* free, don't clunk: failed to establish */
339 mf->path->sfid = nil;
340 putfid(mf);
341 }
342 }
343
344 void
rwalk(Fcall * t)345 rwalk(Fcall *t)
346 {
347 Fid *mf, *nmf;
348 SFid *sfid;
349 Path *p;
350 char *n;
351 int i;
352
353 mf = findfid(host, t->fid, 0);
354 if(mf == nil)
355 return;
356 if(mf->path){
357 DPRINT(2, "walk from %p %q + %d\n", mf, pathstr(mf->path), t->nwname);
358 }
359 if(mf->path->inval){
360 sendreply(mf->path->inval);
361 return;
362 }
363 nmf = nil;
364 if(t->newfid != t->fid){
365 nmf = findfid(host, t->newfid, 1);
366 if(nmf == nil)
367 return;
368 nmf->qid = mf->qid;
369 if(nmf->path != nil)
370 freepath(nmf->path);
371 mf->path->ref++;
372 nmf->path = mf->path;
373 mf = nmf; /* Walk mf */
374 }
375
376 if(statson &&
377 mf->qid.type == rootqid.type && mf->qid.path == rootqid.path &&
378 t->nwname == 1 && strcmp(t->wname[0], "cfsctl") == 0){
379 /* This is the ctl file */
380 mf->qid = ctlqid;
381 c.rhdr.nwqid = 1;
382 c.rhdr.wqid[0] = ctlqid;
383 sendreply(0);
384 return;
385 }
386
387 /*
388 * need this as an invariant, and it should always be true, because
389 * Twalk.fid should exist and thus have sfid. could compensate by
390 * rewalking from nearest parent with sfid (in the limit, root from attach)
391 */
392 assert(mf->path->sfid != nil);
393
394 if(t->nwname == 0){
395 /* new local fid (if any) refers to existing path, shares its sfid via that path */
396 c.rhdr.nwqid = 0;
397 sendreply(0);
398 return;
399 }
400
401 /* t->nwname != 0 */
402
403 if(localwalk(mf, &c.thdr, &c.rhdr, &p)){
404 /* it all worked or is known to fail (in all or in part) */
405 if(c.rhdr.type == Rerror){
406 DPRINT(2, "walk error: %q\n", c.rhdr.ename);
407 sendreply(c.rhdr.ename);
408 if(nmf != nil)
409 putfid(nmf);
410 return;
411 }
412 if(c.rhdr.nwqid != t->nwname){
413 DPRINT(2, "partial walk, hit error\n");
414 sendreply(0);
415 if(nmf != nil)
416 putfid(nmf);
417 return;
418 }
419 DPRINT(2, "seen it: %q %q\n", pathstr(p), p->inval);
420 if(p->sfid != nil){
421 p->ref++;
422 freepath(mf->path);
423 mf->path = p;
424 if(c.rhdr.nwqid > 0)
425 mf->qid = c.rhdr.wqid[c.rhdr.nwqid-1];
426 sendreply(0);
427 return;
428 }
429 /* know the path, but need a fid on the server */
430 }
431
432 /* walk to a new server fid (ie, server always sees fid != newfid) */
433 sfid = allocsfid(); /* must release it, if walk doesn't work */
434
435 if(delegate(t, mf, sfid) < 0){ /* complete failure */
436 if(t->nwname > 0){ /* cache first item's failure */
437 DPRINT(2, "bad path: %q + %q -> %q\n", pathstr(mf->path), t->wname[0], s.rhdr.ename);
438 badpath(mf->path, t->wname[0], s.rhdr.ename);
439 }
440 if(nmf != nil)
441 putfid(nmf);
442 freesfid(sfid); /* no need to clunk it, since it hasn't been assigned */
443 return;
444 }
445
446 if(s.rhdr.nwqid == t->nwname){ /* complete success */
447 for(i = 0; i < t->nwname; i++){
448 n = t->wname[i];
449 if(strcmp(n, "..") == 0){
450 p = mf->path->parent;
451 if(p != nil){ /* otherwise at root, no change */
452 p->ref++;
453 freepath(mf->path);
454 mf->path = p;
455 }
456 }else
457 mf->path = newpath(mf->path, n, s.rhdr.wqid[i]);
458 }
459 mf->qid = s.rhdr.wqid[s.rhdr.nwqid-1]; /* nwname != 0 (above) */
460 if(mf->path->sfid != nil){
461 /* shouldn't happen (otherwise we wouldn't walk, because localwalk would find it) */
462 sysfatal("rwalk: unexpected sfid: %q -> %ud\n", pathstr(mf->path), mf->path->sfid->fid);
463 }
464 mf->path->sfid = sfid;
465 return;
466 }
467
468 /* partial success; note success and failure, and release fid */
469 p = mf->path;
470 for(i = 0; i < s.rhdr.nwqid; i++){
471 n = t->wname[i];
472 if(strcmp(n, "..") == 0){
473 if(p->parent != nil)
474 p = p->parent;
475 }else
476 p = newpath(p, n, s.rhdr.wqid[i]);
477 }
478 n = t->wname[i];
479 if(i == 0 || s.rhdr.wqid[i-1].type & QTDIR)
480 badpath(p, n, Enonexist);
481 if(nmf != nil)
482 putfid(nmf);
483 freesfid(sfid);
484 }
485
486 int
localwalk(Fid * mf,Fcall * t,Fcall * r,Path ** pp)487 localwalk(Fid *mf, Fcall *t, Fcall *r, Path **pp)
488 {
489 Path *p, *q;
490 char *n, *err;
491 int i;
492
493 *pp = nil;
494 if(t->nwname == 0)
495 return 0; /* clone */
496 if(mf->path == nil)
497 return 0;
498 r->type = Rwalk;
499 r->nwqid = 0;
500 p = mf->path;
501 for(i = 0; i < t->nwname; i++){
502 n = t->wname[i];
503 if((err = p->inval) != nil)
504 goto Err;
505 if((p->qid.type & QTDIR) == 0){
506 err = Enotdir;
507 goto Err;
508 }
509 if(strcmp(n, "..") == 0){
510 if((q = p->parent) == nil)
511 q = p;
512 }else{
513 for(q = p->child; q != nil; q = q->next){
514 if(strcmp(n, q->name) == 0){
515 if((err = q->inval) != nil)
516 goto Err;
517 break;
518 }
519 }
520 if(q == nil)
521 return 0; /* not found in cache, must try server */
522 }
523 r->wqid[r->nwqid++] = q->qid;
524 p = q;
525 }
526 /* found them all: if p's not locally NOFID, link incoming fid to it as local value */
527 *pp = p;
528 return 1;
529
530 Err:
531 if(r->nwqid == 0){
532 r->type = Rerror;
533 r->ename = err;
534 }
535 return 1;
536 }
537
538 void
ropen(Fcall * t)539 ropen(Fcall *t)
540 {
541 Fid *mf;
542 SFid *sfid;
543 int omode;
544 File *file;
545
546 mf = findfid(host, t->fid, 0);
547 if(mf == nil)
548 return;
549 if(mf->opened != nil){
550 sendreply(Eopened);
551 return;
552 }
553 omode = openmode(t->mode);
554 if(omode < 0){
555 sendreply(Emode);
556 return;
557 }
558 if(statson && ctltest(mf)){
559 /* Opening ctl file */
560 if(t->mode != OREAD){
561 sendreply("permission denied");
562 return;
563 }
564 mf->mode = OREAD;
565 c.rhdr.qid = ctlqid;
566 c.rhdr.iounit = 0;
567 sendreply(0);
568 genstats();
569 return;
570 }
571 if(t->mode & (ORCLOSE|OTRUNC) || isdirect(mf->qid)){
572 /* must have private sfid */
573 DPRINT(2, "open dir/auth: %q\n", pathstr(mf->path));
574 sfid = sfclone(mf->path->sfid);
575 if(delegate(t, mf, sfid) < 0){
576 sfclunk(sfid);
577 return;
578 }
579 mf->qid = s.rhdr.qid;
580 /* don't currently need iounit */
581 }else if((sfid = alreadyopen(mf, omode)) == nil){
582 sfid = sfclone(mf->path->sfid);
583 DPRINT(2, "new open %q -> %ud\n", pathstr(mf->path), sfid->fid);
584 if(delegate(t, mf, sfid) < 0){
585 sfclunk(sfid);
586 return;
587 }
588 if(mf->qid.type != s.rhdr.qid.type || mf->qid.path != s.rhdr.qid.path){
589 /* file changed type or naming is volatile */
590 print("file changed underfoot: %q %#ux %llud -> %#ux %llud\n",
591 pathstr(mf->path), mf->qid.type, mf->qid.path, s.rhdr.qid.type, s.rhdr.qid.path);
592 mf->path->qid = mf->qid;
593 fileinval(mf->path);
594 }
595 mf->qid = s.rhdr.qid; /* picks up vers */
596 openfile(mf, omode, s.rhdr.iounit, sfid);
597 }else{
598 DPRINT(2, "cached open %q -> %ud\n", pathstr(mf->path), sfid->fid);
599 c.rhdr.qid = mf->path->file->qid;
600 c.rhdr.iounit = mf->path->file->iounit;
601 sendreply(0);
602 }
603 mf->opened = sfid;
604 mf->mode = omode;
605 if(t->mode & OTRUNC && !(mf->qid.type & QTAPPEND)){
606 file = mf->path->file;
607 if(file != nil){
608 cacheinval(file); /* discard cached data */
609 file->length = 0;
610 }
611 }
612 }
613
614 void
rcreate(Fcall * t)615 rcreate(Fcall *t)
616 {
617 Fid *mf;
618 SFid *sfid;
619 int omode;
620
621 mf = findfid(host, t->fid, 0);
622 if(mf == nil)
623 return;
624 if(statson && ctltest(mf)){
625 sendreply(Eexist);
626 return;
627 }
628 omode = openmode(t->mode);
629 if(omode < 0){
630 sendreply(Emode);
631 return;
632 }
633 /* always write-through, and by definition can't exist and be open */
634 sfid = sfclone(mf->path->sfid);
635 if(delegate(t, mf, sfid) == 0){
636 mf->opened = sfid;
637 mf->mode = omode;
638 mf->qid = s.rhdr.qid;
639 mf->path = newpath(mf->path, t->name, mf->qid);
640 if(!(t->mode & ORCLOSE || isdirect(mf->qid))){
641 /* can cache (if it's volatile, find out on subsequent open) */
642 openfile(mf, omode, s.rhdr.iounit, sfid);
643 }
644 //mf->iounit = s.rhdr.iounit;
645 }else
646 sfclunk(sfid);
647 }
648
649 void
rclunk(Fcall * t)650 rclunk(Fcall *t)
651 {
652 Fid *mf;
653
654 mf = findfid(host, t->fid, 0);
655 if(mf == nil)
656 return;
657 if(mf->opened != nil)
658 closefile(mf, 1, 0);
659 /* local clunk, not delegated */
660 sendreply(0);
661 putfid(mf);
662 }
663
664 void
rremove(Fcall * t)665 rremove(Fcall *t)
666 {
667 Fid *mf;
668 Path *p;
669 SFid *sfid;
670 File *file;
671
672 mf = findfid(host, t->fid, 0);
673 if(mf == nil)
674 return;
675 /* invalidate path entry; discard file; discard any local fids in use */
676 if(statson && ctltest(mf)){
677 sendreply("not removed");
678 return;
679 }
680 file = nil;
681 p = mf->path;
682 if(delegate(t, mf, nil) == 0){
683 setinval(p, Eremoved);
684 file = p->file;
685 p->file = nil;
686 }
687 /* in any case, the fid was clunked: free the sfid */
688 if(mf->opened == nil){
689 sfid = p->sfid;
690 p->sfid = nil;
691 freesfid(sfid);
692 }else
693 closefile(mf, 0, 1);
694 putfid(mf);
695 if(file != nil)
696 putfile(file);
697 }
698
699 void
rread(Fcall * t)700 rread(Fcall *t)
701 {
702 Fid *mf;
703 int cnt, done;
704 long n;
705 vlong off, first;
706 char *cp;
707 File *file;
708 char data[MAXFDATA];
709
710 mf = findfid(host, t->fid, 0);
711 if(mf == nil)
712 return;
713
714 off = t->offset;
715 cnt = t->count;
716
717 if(statson && ctltest(mf)){
718 if(cnt > statlen-off)
719 c.rhdr.count = statlen-off;
720 else
721 c.rhdr.count = cnt;
722 if((int)c.rhdr.count < 0){
723 c.rhdr.count = 0;
724 sendreply(0);
725 return;
726 }
727 c.rhdr.data = statbuf + off;
728 sendreply(0);
729 return;
730 }
731
732 if(mf->opened == nil || mf->mode == OWRITE){
733 sendreply("not open for reading");
734 return;
735 }
736
737 file = mf->path->file;
738 if(isdirect(mf->qid) || file == nil){
739 DPRINT(2, "delegating read\n");
740 delegate(t, mf, nil);
741 if(statson){
742 if(mf->qid.type & QTDIR)
743 cfsstat.ndirread++;
744 else
745 cfsstat.ndelegateread++;
746 if(s.rhdr.count > 0){
747 cfsstat.bytesread += s.rhdr.count;
748 if(mf->qid.type & QTDIR)
749 cfsstat.bytesfromdirs += s.rhdr.count;
750 else
751 cfsstat.bytesfromserver += s.rhdr.count;
752 }
753 }
754 return;
755 }
756
757 first = off;
758 cp = data;
759 done = 0;
760 while(cnt>0 && !done){
761 if(off >= file->clength){
762 DPRINT(2, "offset %lld >= length %lld\n", off, file->clength);
763 break;
764 }
765 n = cacheread(file, cp, off, cnt);
766 if(n <= 0){
767 n = -n;
768 if(n==0 || n>cnt)
769 n = cnt;
770 DPRINT(2, "fetch %ld bytes of data from server at offset %lld\n", n, off);
771 s.thdr.tag = t->tag;
772 s.thdr.type = t->type;
773 s.thdr.fid = t->fid;
774 s.thdr.offset = off;
775 s.thdr.count = n;
776 if(statson)
777 cfsstat.ndelegateread++;
778 if(askserver(t, mf) < 0){
779 sendreply(s.rhdr.ename);
780 return;
781 }
782 if(s.rhdr.count != n)
783 done = 1;
784 n = s.rhdr.count;
785 if(n == 0){
786 /* end of file */
787 if(file->clength > off){
788 DPRINT(2, "file %llud.%ld, length %lld\n",
789 file->qid.path,
790 file->qid.vers, off);
791 file->clength = off;
792 }
793 break;
794 }
795 memmove(cp, s.rhdr.data, n);
796 cachewrite(file, cp, off, n);
797 if(statson){
798 cfsstat.bytestocache += n;
799 cfsstat.bytesfromserver += n;
800 }
801 }else{
802 DPRINT(2, "fetched %ld bytes from cache\n", n);
803 if(statson)
804 cfsstat.bytesfromcache += n;
805 }
806 cnt -= n;
807 off += n;
808 cp += n;
809 }
810 c.rhdr.data = data;
811 c.rhdr.count = off - first;
812 if(statson)
813 cfsstat.bytesread += c.rhdr.count;
814 sendreply(0);
815 }
816
817 void
rwrite(Fcall * t)818 rwrite(Fcall *t)
819 {
820 Fid *mf;
821 File *file;
822 u32int v1, v2, count;
823
824 mf = findfid(host, t->fid, 0);
825 if(mf == nil)
826 return;
827 if(statson && ctltest(mf)){
828 sendreply("read only");
829 return;
830 }
831 if(mf->opened == nil || mf->mode == OREAD){
832 sendreply("not open for writing");
833 return;
834 }
835 file = mf->path->file;
836 if(isdirect(mf->qid) || file == nil){
837 delegate(t, mf, nil);
838 if(statson && s.rhdr.count > 0)
839 cfsstat.byteswritten += s.rhdr.count;
840 return;
841 }
842
843 /* add to local cache as write through */
844 if(delegate(t, mf, nil) < 0)
845 return;
846
847 count = s.rhdr.count;
848 cfsstat.byteswritten += count;
849
850 v1 = mf->qid.vers + 1;
851 v2 = mf->path->qid.vers + 1;
852 if(v1 > v2)
853 v1 = v2;
854 mf->qid.vers = v1;
855 mf->path->qid.vers = v1;
856 file->qid.vers = v1;
857 if(file->clength < t->offset + count)
858 file->clength = t->offset + count;
859 cachewrite(file, t->data, t->offset, count);
860 }
861
862 void
rstat(Fcall * t)863 rstat(Fcall *t)
864 {
865 Fid *mf;
866 Dir d;
867 uchar statbuf[256];
868
869 mf = findfid(host, t->fid, 0);
870 if(mf == nil)
871 return;
872 if(statson && ctltest(mf)){
873 genstats();
874 d.qid = ctlqid;
875 d.mode = 0444;
876 d.length = statlen;
877 d.name = "cfsctl";
878 d.uid = "none";
879 d.gid = "none";
880 d.muid = "none";
881 d.atime = time(nil);
882 d.mtime = d.atime;
883 c.rhdr.stat = statbuf;
884 c.rhdr.nstat = convD2M(&d, statbuf, sizeof(statbuf));
885 sendreply(0);
886 return;
887 }
888 if(delegate(t, mf, nil) == 0){
889 convM2D(s.rhdr.stat, s.rhdr.nstat , &d, nil);
890 //mf->qid = d.qid;
891 if(mf->path->file != nil)
892 copystat(mf->path->file, &d, 0);
893 }
894 }
895
896 void
rwstat(Fcall * t)897 rwstat(Fcall *t)
898 {
899 Fid *mf;
900 Path *p;
901 Dir *d;
902 int qt;
903
904 mf = findfid(host, t->fid, 0);
905 if(mf == nil)
906 return;
907 if(statson && ctltest(mf)){
908 sendreply("read only");
909 return;
910 }
911 if(delegate(t, mf, nil) == 0){
912 d = malloc(sizeof(*d)+t->nstat);
913 if(convM2D(t->stat, t->nstat, d, (char*)(d+1)) != 0){
914 if(*d->name){ /* file renamed */
915 p = mf->path;
916 free(p->name);
917 p->name = strdup(d->name);
918 freeinval(p);
919 }
920 if(d->mode != ~0){
921 qt = d->mode>>24;
922 mf->qid.type = qt;
923 mf->path->qid.type = qt;
924 }
925 if(mf->path->file != nil)
926 copystat(mf->path->file, d, 1);
927 }
928 free(d);
929 }
930 }
931
932 int
openmode(uint o)933 openmode(uint o)
934 {
935 uint m;
936
937 m = o & ~(OTRUNC|OCEXEC|ORCLOSE);
938 if(m > OEXEC)
939 return -1;
940 if(m == OEXEC)
941 m = OREAD;
942 if(o&OTRUNC && m == OREAD)
943 return -1;
944 return m;
945 }
946
947 void
error(char * fmt,...)948 error(char *fmt, ...)
949 {
950 va_list arg;
951 static char buf[2048];
952
953 va_start(arg, fmt);
954 vseprint(buf, buf+sizeof(buf), fmt, arg);
955 va_end(arg);
956 fprint(2, "%s: %s\n", argv0, buf);
957 threadexitsall("error");
958 }
959
960 void
warning(char * s)961 warning(char *s)
962 {
963 fprint(2, "%s: %s: %r\n", argv0, s);
964 }
965
966 /*
967 * send a reply to the client
968 */
969 void
sendreply(char * err)970 sendreply(char *err)
971 {
972 if(err){
973 c.rhdr.type = Rerror;
974 c.rhdr.ename = err;
975 }else{
976 c.rhdr.type = c.thdr.type+1;
977 c.rhdr.fid = c.thdr.fid;
978 }
979 c.rhdr.tag = c.thdr.tag;
980 sendmsg(&c, &c.rhdr);
981 }
982
983 /*
984 * local fids
985 */
986 Fid*
findfid(Host * host,u32int fid,int mk)987 findfid(Host *host, u32int fid, int mk)
988 {
989 Fid *f, **l;
990
991 if(fid == NOFID){
992 sendreply(Ebadfid);
993 return nil;
994 }
995 l = &host->fids[fid & (FidHash-1)];
996 for(f = *l; f != nil; f = f->next){
997 if(f->fid == fid){
998 if(mk){
999 sendreply(Efidinuse);
1000 return nil;
1001 }
1002 return f;
1003 }
1004 }
1005 if(!mk){
1006 sendreply(Ebadfid);
1007 return nil;
1008 }
1009 f = mallocz(sizeof(*f), 1);
1010 f->fid = fid;
1011 f->next = *l;
1012 *l = f;
1013 return f;
1014 }
1015
1016 void
putfid(Fid * f)1017 putfid(Fid *f)
1018 {
1019 Fid **l;
1020 static Qid zeroqid;
1021
1022 for(l = &host->fids[f->fid & (FidHash-1)]; *l != nil; l = &(*l)->next){
1023 if(*l == f){
1024 *l = f->next;
1025 break;
1026 }
1027 }
1028 if(f->opened != nil){
1029 sfclunk(f->opened);
1030 f->opened = nil;
1031 }
1032 freepath(f->path);
1033 /* poison values just in case */
1034 f->path = nil;
1035 f->fid = NOFID;
1036 f->qid = zeroqid;
1037 free(f);
1038 }
1039
1040 /*
1041 * return server fid for local fid
1042 */
1043 u32int
mapfid(Fid * f)1044 mapfid(Fid *f)
1045 {
1046 if(f->opened != nil){
1047 DPRINT(2, "mapfid: use open fid %ud -> %ud\n", f->fid, f->opened->fid);
1048 return f->opened->fid;
1049 }
1050 if(f->path->sfid == nil)
1051 sysfatal("mapfid: missing sfid for path %q\n", pathstr(f->path));
1052 return f->path->sfid->fid;
1053 }
1054
1055 /*
1056 * send a request to the server, get the reply,
1057 * and send that to the client
1058 */
1059 int
delegate(Fcall * t,Fid * f1,SFid * f2)1060 delegate(Fcall *t, Fid *f1, SFid *f2)
1061 {
1062 int type;
1063
1064 type = t->type;
1065 if(statson){
1066 cfsstat.sm[type].n++;
1067 cfsstat.sm[type].s = nsec();
1068 }
1069
1070 switch(type){
1071 case Tversion:
1072 case Tflush: /* no fid */
1073 break;
1074 case Tauth: /* afid */
1075 t->afid = mapfid(f1);
1076 break;
1077 case Tattach: /* fid, afid */
1078 t->fid = mapfid(f1);
1079 if(f2 != nil)
1080 t->afid = f2->fid;
1081 else
1082 t->afid = NOFID;
1083 break;
1084 case Twalk: /* fid, newfid */
1085 t->fid = mapfid(f1);
1086 t->newfid = f2->fid;
1087 break;
1088 default: /* fid */
1089 if(f2 != nil)
1090 t->fid = f2->fid;
1091 else
1092 t->fid = mapfid(f1);
1093 break;
1094 }
1095
1096 sendmsg(&s, t);
1097 rcvmsg(&s, &s.rhdr);
1098
1099 if(statson)
1100 cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s;
1101
1102 sendmsg(&c, &s.rhdr);
1103 return t->type+1 == s.rhdr.type? 0: -1;
1104 }
1105
1106 /*
1107 * send an i/o request to the server and get a reply
1108 */
1109 int
askserver(Fcall * t,Fid * f)1110 askserver(Fcall *t, Fid *f)
1111 {
1112 int type;
1113
1114 s.thdr.tag = t->tag;
1115
1116 type = s.thdr.type;
1117 if(statson){
1118 cfsstat.sm[type].n++;
1119 cfsstat.sm[type].s = nsec();
1120 }
1121
1122 s.thdr.fid = mapfid(f);
1123 sendmsg(&s, &s.thdr);
1124 rcvmsg(&s, &s.rhdr);
1125
1126 if(statson)
1127 cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s;
1128
1129 return s.thdr.type+1 == s.rhdr.type ? 0 : -1;
1130 }
1131
1132 /*
1133 * send/receive messages with logging
1134 */
1135
1136 void
sendmsg(P9fs * p,Fcall * f)1137 sendmsg(P9fs *p, Fcall *f)
1138 {
1139 DPRINT(2, "->%s: %F\n", p->name, f);
1140
1141 p->len = convS2M(f, p->sndbuf, messagesize);
1142 if(p->len <= 0)
1143 error("convS2M");
1144 if(write(p->fd[1], p->sndbuf, p->len)!=p->len)
1145 error("sendmsg");
1146 }
1147
1148 void
rcvmsg(P9fs * p,Fcall * f)1149 rcvmsg(P9fs *p, Fcall *f)
1150 {
1151 int rlen;
1152 char buf[128];
1153
1154 p->len = read9pmsg(p->fd[0], p->rcvbuf[p->cb], sizeof(p->rcvbuf[0]));
1155 if(p->len <= 0){
1156 snprint(buf, sizeof buf, "read9pmsg(%d)->%ld: %r",
1157 p->fd[0], p->len);
1158 error(buf);
1159 }
1160
1161 if((rlen = convM2S(p->rcvbuf[p->cb], p->len, f)) != p->len)
1162 error("rcvmsg format error, expected length %d, got %d",
1163 rlen, p->len);
1164 DPRINT(2, "<-%s: %F\n", p->name, f);
1165 p->cb = (p->cb+1)%nelem(p->rcvbuf);
1166 }
1167
1168 void
dump(void * a,int len)1169 dump(void *a, int len)
1170 {
1171 uchar *p;
1172
1173 p = a;
1174 fprint(2, "%d bytes", len);
1175 while(len-- > 0)
1176 fprint(2, " %.2ux", *p++);
1177 fprint(2, "\n");
1178 }
1179
1180 /*
1181 * requests (later, unused now)
1182 */
1183
1184 void
readtmsgproc(void * a)1185 readtmsgproc(void *a)
1186 {
1187 //uint messagesize;
1188 int n;
1189 Req *r;
1190 Channel *reqs;
1191
1192 reqs = a;
1193 for(;;){
1194 r = malloc(sizeof(*r)+messagesize);
1195 r->msize = messagesize;
1196 r->buf = (uchar*)(r+1);
1197 n = read9pmsg(c.fd[0], r->buf, r->msize);
1198 if(n <= 0){
1199 free(r);
1200 sendp(reqs, nil);
1201 threadexits(nil);
1202 }
1203 if(convM2S(r->buf, n, &r->t) != n){
1204 free(r);
1205 error("convM2S error in readtmsgproc");
1206 }
1207 sendp(reqs, r);
1208 }
1209 }
1210
1211 void
readrmsgproc(void * a)1212 readrmsgproc(void *a)
1213 {
1214 //uint messagesize;
1215 int n;
1216 Fcall *r;
1217 uchar *buf;
1218 Channel *reps;
1219
1220 reps = a;
1221 for(;;){
1222 r = malloc(sizeof(*r)+messagesize);
1223 buf = (uchar*)(r+1);
1224 n = read9pmsg(c.fd[0], buf, messagesize);
1225 if(n <= 0){
1226 free(r);
1227 sendp(reps, nil);
1228 threadexits(nil);
1229 }
1230 if(convM2S(buf, n, r) != n){
1231 free(r);
1232 error("convM2S error in readtmsgproc");
1233 }
1234 sendp(reps, r);
1235 }
1236 }
1237
1238 void
freereq(Req * r)1239 freereq(Req *r)
1240 {
1241 free(r);
1242 }
1243
1244 /*
1245 * server fids
1246 */
1247
1248 SFid*
allocsfid(void)1249 allocsfid(void)
1250 {
1251 SFid *sf;
1252
1253 sf = freefids;
1254 if(sf != nil){
1255 freefids = sf->next;
1256 sf->ref = 1;
1257 return sf;
1258 }
1259 sf = mallocz(sizeof(*sf), 1);
1260 sf->ref = 1;
1261 sf->fid = ++fidgen;
1262 return sf;
1263 }
1264
1265 void
freesfid(SFid * sf)1266 freesfid(SFid *sf)
1267 {
1268 if(--sf->ref != 0)
1269 return;
1270 /* leave sf->fid alone */
1271 sf->next = freefids;
1272 freefids = sf;
1273 }
1274
1275 SFid*
sfclone(SFid * sf)1276 sfclone(SFid *sf)
1277 {
1278 Fcall t, r;
1279 SFid *cf;
1280
1281 cf = allocsfid();
1282 t.tag = c.thdr.tag;
1283 t.type = Twalk;
1284 t.fid = sf->fid;
1285 t.newfid = cf->fid;
1286 t.nwname = 0;
1287 sendmsg(&s, &t);
1288 rcvmsg(&s, &r);
1289 if(r.type == Rerror){
1290 werrstr("%s", r.ename);
1291 return nil;
1292 }
1293 assert(r.type == Rwalk); /* TO DO: out of order */
1294 return cf;
1295 }
1296
1297 Dir*
sfstat(Path * p,u32int tag)1298 sfstat(Path *p, u32int tag)
1299 {
1300 Fcall t, r;
1301 Dir *d;
1302
1303 assert(p->sfid != nil);
1304 t.tag = tag;
1305 t.type = Tstat;
1306 t.fid = p->sfid->fid;
1307 sendmsg(&s, &t);
1308 rcvmsg(&s, &r);
1309 if(r.type == Rerror){
1310 werrstr("%s", r.ename);
1311 return nil;
1312 }
1313 d = malloc(sizeof(*d)+r.nstat);
1314 if(convM2D(r.stat, r.nstat, d, (char*)(d+1)) == 0){
1315 free(d);
1316 werrstr("invalid stat data");
1317 return nil;
1318 }
1319 return d;
1320 }
1321
1322 void
sfclunk(SFid * sf)1323 sfclunk(SFid *sf)
1324 {
1325 Fcall t, r;
1326
1327 if(sf->ref > 1)
1328 return;
1329 t.tag = c.thdr.tag;
1330 t.type = Tclunk;
1331 t.fid = sf->fid;
1332 sendmsg(&s, &t);
1333 rcvmsg(&s, &r);
1334 /* don't care about result */
1335 }
1336
1337 void
printpath(Path * p,int level)1338 printpath(Path *p, int level)
1339 {
1340 Path *q;
1341
1342 for(int i = 0; i < level; i++)
1343 print("\t");
1344 print("%q [%lud] %q\n", p->name, p->ref, p->inval?p->inval:"");
1345 for(q = p->child; q != nil; q = q->next)
1346 printpath(q, level+1);
1347 }
1348
1349 int
ctltest(Fid * mf)1350 ctltest(Fid *mf)
1351 {
1352 return mf->qid.type == ctlqid.type && mf->qid.path == ctlqid.path;
1353 }
1354
1355 char *mname[]={
1356 [Tversion] "Tversion",
1357 [Tauth] "Tauth",
1358 [Tflush] "Tflush",
1359 [Tattach] "Tattach",
1360 [Twalk] "Twalk",
1361 [Topen] "Topen",
1362 [Tcreate] "Tcreate",
1363 [Tclunk] "Tclunk",
1364 [Tread] "Tread",
1365 [Twrite] "Twrite",
1366 [Tremove] "Tremove",
1367 [Tstat] "Tstat",
1368 [Twstat] "Twstat",
1369 [Rversion] "Rversion",
1370 [Rauth] "Rauth",
1371 [Rerror] "Rerror",
1372 [Rflush] "Rflush",
1373 [Rattach] "Rattach",
1374 [Rwalk] "Rwalk",
1375 [Ropen] "Ropen",
1376 [Rcreate] "Rcreate",
1377 [Rclunk] "Rclunk",
1378 [Rread] "Rread",
1379 [Rwrite] "Rwrite",
1380 [Rremove] "Rremove",
1381 [Rstat] "Rstat",
1382 [Rwstat] "Rwstat",
1383 0,
1384 };
1385
1386 void
genstats(void)1387 genstats(void)
1388 {
1389 int i;
1390 char *p;
1391
1392 p = statbuf;
1393
1394 p += snprint(p, sizeof statbuf+statbuf-p,
1395 " Client Server\n");
1396 p += snprint(p, sizeof statbuf+statbuf-p,
1397 " #calls Δ ms/call Δ #calls Δ ms/call Δ\n");
1398 for(i = 0; i < nelem(cfsstat.cm); i++)
1399 if(cfsstat.cm[i].n || cfsstat.sm[i].n){
1400 p += snprint(p, sizeof statbuf+statbuf-p,
1401 "%7lud %7lud ", cfsstat.cm[i].n,
1402 cfsstat.cm[i].n - cfsprev.cm[i].n);
1403 if(cfsstat.cm[i].n)
1404 p += snprint(p, sizeof statbuf+statbuf-p,
1405 "%7.3f ", 0.000001*cfsstat.cm[i].t/
1406 cfsstat.cm[i].n);
1407 else
1408 p += snprint(p, sizeof statbuf+statbuf-p,
1409 " ");
1410 if(cfsstat.cm[i].n - cfsprev.cm[i].n)
1411 p += snprint(p, sizeof statbuf+statbuf-p,
1412 "%7.3f ", 0.000001*
1413 (cfsstat.cm[i].t - cfsprev.cm[i].t)/
1414 (cfsstat.cm[i].n - cfsprev.cm[i].n));
1415 else
1416 p += snprint(p, sizeof statbuf+statbuf-p,
1417 " ");
1418 p += snprint(p, sizeof statbuf+statbuf-p,
1419 "%7lud %7lud ", cfsstat.sm[i].n,
1420 cfsstat.sm[i].n - cfsprev.sm[i].n);
1421 if(cfsstat.sm[i].n)
1422 p += snprint(p, sizeof statbuf+statbuf-p,
1423 "%7.3f ", 0.000001*cfsstat.sm[i].t/
1424 cfsstat.sm[i].n);
1425 else
1426 p += snprint(p, sizeof statbuf+statbuf-p,
1427 " ");
1428 if(cfsstat.sm[i].n - cfsprev.sm[i].n)
1429 p += snprint(p, sizeof statbuf+statbuf-p,
1430 "%7.3f ", 0.000001*
1431 (cfsstat.sm[i].t - cfsprev.sm[i].t)/
1432 (cfsstat.sm[i].n - cfsprev.sm[i].n));
1433 else
1434 p += snprint(p, sizeof statbuf+statbuf-p,
1435 " ");
1436 p += snprint(p, sizeof statbuf+statbuf-p, "%s\n",
1437 mname[i]);
1438 }
1439 p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndirread\n",
1440 cfsstat.ndirread, cfsstat.ndirread - cfsprev.ndirread);
1441 p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndelegateread\n",
1442 cfsstat.ndelegateread, cfsstat.ndelegateread -
1443 cfsprev.ndelegateread);
1444 p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ninsert\n",
1445 cfsstat.ninsert, cfsstat.ninsert - cfsprev.ninsert);
1446 p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud ndelete\n",
1447 cfsstat.ndelete, cfsstat.ndelete - cfsprev.ndelete);
1448 p += snprint(p, sizeof statbuf+statbuf-p, "%7lud %7lud nupdate\n",
1449 cfsstat.nupdate, cfsstat.nupdate - cfsprev.nupdate);
1450
1451 p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesread\n",
1452 cfsstat.bytesread, cfsstat.bytesread - cfsprev.bytesread);
1453 p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud byteswritten\n",
1454 cfsstat.byteswritten, cfsstat.byteswritten -
1455 cfsprev.byteswritten);
1456 p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromserver\n",
1457 cfsstat.bytesfromserver, cfsstat.bytesfromserver -
1458 cfsprev.bytesfromserver);
1459 p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromdirs\n",
1460 cfsstat.bytesfromdirs, cfsstat.bytesfromdirs -
1461 cfsprev.bytesfromdirs);
1462 p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytesfromcache\n",
1463 cfsstat.bytesfromcache, cfsstat.bytesfromcache -
1464 cfsprev.bytesfromcache);
1465 p += snprint(p, sizeof statbuf+statbuf-p, "%7llud %7llud bytestocache\n",
1466 cfsstat.bytestocache, cfsstat.bytestocache -
1467 cfsprev.bytestocache);
1468 statlen = p - statbuf;
1469 cfsprev = cfsstat;
1470 }
1471
1472 /*
1473 * paths
1474 */
1475
1476 Path*
newpath(Path * parent,char * name,Qid qid)1477 newpath(Path *parent, char *name, Qid qid)
1478 {
1479 Path *p;
1480
1481 if(parent != nil){
1482 for(p = parent->child; p != nil; p = p->next){
1483 if(strcmp(p->name, name) == 0){
1484 if(p->inval != nil){
1485 free(p->inval);
1486 p->inval = nil;
1487 }
1488 p->qid = qid;
1489 p->ref++;
1490 return p;
1491 }
1492 }
1493 }
1494 p = mallocz(sizeof(*p), 1);
1495 p->ref = 2;
1496 if(name != nil)
1497 p->name = strdup(name);
1498 p->qid = qid;
1499 p->parent = parent;
1500 if(parent != nil){
1501 parent->ref++;
1502 p->next = parent->child;
1503 parent->child = p;
1504 }
1505 return p;
1506 }
1507
1508 void
freeinval(Path * p)1509 freeinval(Path *p)
1510 {
1511 Path *q, **r;
1512
1513 for(r = &p->parent->child; (q = *r) != nil; r = &q->next){
1514 if(q == p)
1515 continue;
1516 if(strcmp(q->name, p->name) == 0 && q->inval != nil){
1517 if(q->ref > 1){
1518 *r = q->next;
1519 q->next = p->next;
1520 p->next = q;
1521 }
1522 freepath(q);
1523 break;
1524 }
1525 }
1526 }
1527
1528 void
setinval(Path * p,char * err)1529 setinval(Path *p, char *err)
1530 {
1531 if(p->inval != nil){
1532 free(p->inval);
1533 p->inval = nil;
1534 }
1535 if(err != nil)
1536 p->inval = strdup(err);
1537 }
1538
1539 void
badpath(Path * parent,char * name,char * err)1540 badpath(Path *parent, char *name, char *err)
1541 {
1542 Path *p;
1543
1544 for(p = parent->child; p != nil; p = p->next){
1545 if(strcmp(p->name, name) == 0){
1546 setinval(p, err);
1547 return;
1548 }
1549 }
1550 p = mallocz(sizeof(*p), 1);
1551 p->ref = 2;
1552 if(name != nil)
1553 p->name = strdup(name);
1554 p->inval = strdup(err);
1555 p->parent = parent;
1556 if(parent != nil){
1557 parent->ref++;
1558 p->next = parent->child;
1559 parent->child = p;
1560 }
1561 }
1562
1563 void
fileinval(Path * p)1564 fileinval(Path *p)
1565 {
1566 if(p->file != nil){
1567 putfile(p->file);
1568 p->file = nil;
1569 }
1570 }
1571
1572 void
freepath(Path * p)1573 freepath(Path *p)
1574 {
1575 Path *q, **r;
1576
1577 while(p != nil && --p->ref == 0){
1578 if(p->child != nil)
1579 error("freepath child");
1580 q = p->parent;
1581 if(q != nil){
1582 for(r = &q->child; *r != nil; r = &(*r)->next){
1583 if(*r == p){
1584 *r = p->next;
1585 break;
1586 }
1587 }
1588 }
1589 if(p->sfid != nil){
1590 /* TO DO: could queue these for a great clunking proc */
1591 sfclunk(p->sfid);
1592 p->sfid = nil;
1593 }
1594 if(p->inval != nil)
1595 free(p->inval);
1596 if(p->file != nil)
1597 putfile(p->file);
1598 free(p->name);
1599 free(p);
1600 p = q;
1601 }
1602 }
1603
1604 static char*
pathstr1(Path * p,char * ep)1605 pathstr1(Path *p, char *ep)
1606 {
1607 ep -= strlen(p->name);
1608 memmove(ep, p->name, strlen(p->name));
1609 if(p->parent != nil){
1610 *--ep = '/';
1611 ep = pathstr1(p->parent, ep);
1612 }
1613 return ep;
1614 }
1615
1616 char*
pathstr(Path * p)1617 pathstr(Path *p)
1618 {
1619 static char buf[1000];
1620 return pathstr1(p, buf+sizeof(buf)-1);
1621 }
1622
1623 /*
1624 * files
1625 */
1626
1627 void
openfile(Fid * mf,int omode,u32int iounit,SFid * sfid)1628 openfile(Fid *mf, int omode, u32int iounit, SFid *sfid)
1629 {
1630 /* open mf and cache open File at p */
1631 Path *p;
1632 File *file;
1633 SFid *osfid;
1634
1635 p = mf->path;
1636 file = p->file;
1637 if(file == nil){
1638 file = mallocz(sizeof(*file), 1);
1639 file->ref = 1;
1640 file->qid = mf->qid; /* TO DO: check for clone files */
1641 file->clength = MAXLEN;
1642 p->file = file;
1643 }
1644 osfid = file->open[omode];
1645 if(osfid != nil){
1646 DPRINT(2, "openfile: existing sfid %ud open %d\n", osfid->fid, omode);
1647 return;
1648 }
1649 DPRINT(2, "openfile: cached %ud for %q mode %d\n", sfid->fid, pathstr(p), omode);
1650 sfid->ref++;
1651 file->open[omode] = sfid;
1652 if(file->iounit == 0)
1653 file->iounit = iounit; /* BUG: might vary */
1654 }
1655
1656 void
closefile(Fid * mf,int mustclunk,int removed)1657 closefile(Fid *mf, int mustclunk, int removed)
1658 {
1659 Path *p;
1660 File *file;
1661 SFid *sfid, *osfid;
1662
1663 if((sfid = mf->opened) == nil)
1664 return;
1665 p = mf->path;
1666 file = p->file;
1667 if(file == nil){ /* TO DO: messy */
1668 mf->opened = nil;
1669 if(sfid->ref == 1 && mustclunk)
1670 sfclunk(sfid);
1671 freesfid(sfid);
1672 return;
1673 }
1674 osfid = file->open[mf->mode];
1675 if(osfid == sfid){
1676 if(removed || sfid->ref == 1)
1677 file->open[mf->mode] = nil;
1678 if(sfid->ref == 1){
1679 if(mustclunk)
1680 sfclunk(sfid);
1681 freesfid(sfid);
1682 }else
1683 sfid->ref--;
1684 }
1685 mf->opened = nil;
1686 }
1687
1688 SFid*
alreadyopen(Fid * mf,uint mode)1689 alreadyopen(Fid *mf, uint mode)
1690 {
1691 File *file;
1692 SFid *sfid;
1693
1694 file = mf->path->file;
1695 if(file == nil)
1696 return nil;
1697 sfid = file->open[mode&3];
1698 if(sfid == nil)
1699 return nil;
1700 DPRINT(2, "openfile: existing sfid %ud open %d\n", sfid->fid, mode&3);
1701 sfid->ref++;
1702 return sfid;
1703 }
1704
1705 void
copystat(File * file,Dir * d,int iswstat)1706 copystat(File *file, Dir *d, int iswstat)
1707 {
1708 if(d->length != ~(vlong)0){
1709 /* length change: discard cached data */
1710 if(iswstat && file->length < file->clength)
1711 cacheinval(file);
1712 file->length = d->length;
1713 }
1714 if(d->mode != ~0){
1715 file->mode = d->mode;
1716 file->qid.type = d->mode>>24;
1717 }
1718 if(d->atime != ~0)
1719 file->atime = d->atime;
1720 if(d->mtime != ~0)
1721 file->mtime = d->mtime;
1722 if(*d->uid){
1723 freestr(file->uid);
1724 file->uid = newstr(d->uid);
1725 }
1726 if(*d->gid){
1727 freestr(file->gid);
1728 file->gid = newstr(d->gid);
1729 }
1730 if(*d->muid){
1731 freestr(file->muid);
1732 file->muid = newstr(d->muid);
1733 }
1734 }
1735
1736 void
putfile(File * f)1737 putfile(File *f)
1738 {
1739 int i;
1740 SFid *sfid;
1741
1742 if(--f->ref != 0)
1743 return;
1744 for(i = 0; i < nelem(f->open); i++){
1745 sfid = f->open[i];
1746 if(sfid != nil){
1747 f->open[i] = nil;
1748 sfclunk(sfid);
1749 freesfid(sfid);
1750 }
1751 }
1752 freestr(f->uid);
1753 freestr(f->gid);
1754 freestr(f->muid);
1755 cacheinval(f);
1756 free(f->cached);
1757 free(f);
1758 }
1759
1760 /*
1761 * data
1762 */
1763
1764 static int radix[] = {10, 12, 14};
1765 static uint range[] = {8, 32, 0};
1766 static uint cacheused;
1767
1768 void
datainit(void)1769 datainit(void)
1770 {
1771 datalist.forw = datalist.back = &datalist;
1772 }
1773
1774 Data*
allocdata(File * file,uint n,uint nbytes)1775 allocdata(File *file, uint n, uint nbytes)
1776 {
1777 Data *d;
1778
1779 while(cacheused+nbytes > cachesize && datalist.forw != datalist.back)
1780 freedata(datalist.forw);
1781 cacheused += nbytes;
1782 d = mallocz(sizeof(*d), 0);
1783 d->owner = file;
1784 d->n = n;
1785 d->base = malloc(nbytes);
1786 d->size = nbytes;
1787 d->min = 0;
1788 d->max = 0;
1789 d->forw = &datalist;
1790 d->back = datalist.back;
1791 d->back->forw = d;
1792 datalist.back = d;
1793 return d;
1794 }
1795
1796 void
freedata(Data * d)1797 freedata(Data *d)
1798 {
1799 d->forw->back = d->back;
1800 d->back->forw = d->forw;
1801 cacheused -= d->size;
1802 if(d->owner != nil)
1803 d->owner->cached[d->n] = nil;
1804 free(d->base);
1805 free(d);
1806 }
1807
1808 /*
1809 * move recently-used data to end
1810 */
1811 void
usedata(Data * d)1812 usedata(Data *d)
1813 {
1814 if(datalist.back == d)
1815 return; /* already at end */
1816
1817 d->forw->back = d->back;
1818 d->back->forw = d->forw;
1819
1820 d->forw = &datalist;
1821 d->back = datalist.back;
1822 d->back->forw = d;
1823 datalist.back = d;
1824 }
1825
1826 /*
1827 * data cache
1828 */
1829
1830 int
cacheread(File * file,void * buf,vlong offset,int nbytes)1831 cacheread(File *file, void *buf, vlong offset, int nbytes)
1832 {
1833 char *p;
1834 Data *d;
1835 int n, o;
1836
1837 DPRINT(2, "file %lld length %lld\n", file->qid.path, file->clength);
1838 p = buf;
1839 while(nbytes > 0){
1840 d = finddata(file, offset, &o);
1841 if(d == nil)
1842 break;
1843 if(o < d->min){
1844 if(p == (char*)buf)
1845 return -(d->min-o); /* fill the gap */
1846 break;
1847 }else if(o >= d->max)
1848 break;
1849 usedata(d);
1850 /* o >= d->min && o < d->max */
1851 n = nbytes;
1852 if(n > d->max-o)
1853 n = d->max-o;
1854 memmove(p, d->base+o, n);
1855 p += n;
1856 nbytes -= n;
1857 offset += n;
1858 }
1859 return p-(char*)buf;
1860 }
1861
1862 void
cachewrite(File * file,void * buf,vlong offset,int nbytes)1863 cachewrite(File *file, void *buf, vlong offset, int nbytes)
1864 {
1865 char *p;
1866 Data *d;
1867 int n, o;
1868
1869 p = buf;
1870 while(nbytes > 0){
1871 d = storedata(file, offset, &o);
1872 if(d == nil)
1873 break;
1874 n = nbytes;
1875 if(n > d->size-o)
1876 n = d->size-o;
1877 cachemerge(d, p, o, n);
1878 p += n;
1879 offset += n;
1880 nbytes -= n;
1881 }
1882 if(offset > file->clength)
1883 file->clength = offset;
1884 }
1885
1886 /*
1887 * merge data in if it overlaps existing contents,
1888 * or simply replace existing contents otherwise
1889 */
1890 void
cachemerge(Data * p,char * from,int start,int len)1891 cachemerge(Data *p, char *from, int start, int len)
1892 {
1893 int end;
1894
1895 end = start + len;
1896 memmove(p->base+start, from, len);
1897
1898 if(start > p->max || p->min > end){
1899 p->min = start;
1900 p->max = end;
1901 }else{
1902 if(start < p->min)
1903 p->min = start;
1904 if(end > p->max)
1905 p->max = end;
1906 }
1907 }
1908
1909 void
cacheinval(File * file)1910 cacheinval(File *file)
1911 {
1912 Data *d;
1913 int i;
1914
1915 if(file->cached != nil){
1916 for(i = 0; i < file->ndata; i++){
1917 d = file->cached[i];
1918 if(d != nil){
1919 file->cached[i] = nil;
1920 d->owner = nil;
1921 freedata(d);
1922 }
1923 }
1924 /* leave the array */
1925 }
1926 file->clength = 0;
1927 }
1928
1929 Data*
finddata(File * file,uvlong offset,int * blkoff)1930 finddata(File *file, uvlong offset, int *blkoff)
1931 {
1932 int r, x;
1933 uvlong base, o;
1934
1935 x = 0;
1936 base = 0;
1937 for(r = 0; r < nelem(radix); r++){
1938 o = (offset - base) >> radix[r];
1939 DPRINT(2, "file %llud offset %llud x %d base %llud o %llud\n", file->qid.path, offset, x, base, o);
1940 if(range[r] == 0 || o < range[r]){
1941 o += x;
1942 if(o >= file->ndata)
1943 break;
1944 *blkoff = (offset-base) & ((1<<radix[r])-1);
1945 return file->cached[(int)o];
1946 }
1947 base += range[r]<<radix[r];
1948 x += range[r];
1949 }
1950 return nil;
1951 }
1952
1953 Data*
storedata(File * file,uvlong offset,int * blkoff)1954 storedata(File *file, uvlong offset, int *blkoff)
1955 {
1956 int r, x, v, size;
1957 uvlong base, o;
1958 Data **p;
1959
1960 if(file->cached == nil){
1961 file->cached = mallocz(16*sizeof(*file->cached), 1);
1962 file->ndata = 16;
1963 }
1964 x = 0;
1965 base = 0;
1966 for(r = 0; r < nelem(radix); r++){
1967 o = (offset - base) >> radix[r];
1968 DPRINT(2, "store file %llud offset %llud x %d base %llud o %llud\n", file->qid.path, offset, x, base, o);
1969 if(range[r] == 0 || o < range[r]){
1970 o += x;
1971 if(o >= file->ndata){
1972 if(o >= 512)
1973 return nil; /* won't fit */
1974 v = (o+32+31)&~31;
1975 file->cached = realloc(file->cached, v*sizeof(*file->cached));
1976 memset(file->cached+file->ndata, 0, (v-file->ndata)*sizeof(*file->cached));
1977 file->ndata = v;
1978 }
1979 size = 1 << radix[r];
1980 DPRINT(2, " -> %d %d\n", (int)o, size);
1981 *blkoff = (offset-base) & (size-1);
1982 p = &file->cached[(int)o];
1983 if(*p == nil)
1984 *p = allocdata(file, (int)o, size);
1985 else
1986 usedata(*p);
1987 return *p;
1988 }
1989 base += range[r]<<radix[r];
1990 x += range[r];
1991 }
1992 return nil;
1993 }
1994
1995 /*
1996 * Strings
1997 */
1998
1999 enum{
2000 Strhashmask= (1<<8)-1,
2001 };
2002
2003 static struct{
2004 QLock;
2005 String* htab[Strhashmask+1];
2006 } stralloc;
2007
2008 String**
hashslot(char * s)2009 hashslot(char *s)
2010 {
2011 uint h, g;
2012 uchar *p;
2013
2014 h = 0;
2015 for(p = (uchar*)s; *p != 0; p++){
2016 h = (h<<4) + *p;
2017 g = h & (0xF<<28);
2018 if(g != 0)
2019 h ^= ((g>>24) & 0xFF) | g;
2020 }
2021 return &stralloc.htab[h & Strhashmask];
2022 }
2023
2024 String*
newstr(char * val)2025 newstr(char *val)
2026 {
2027 String *s, **l;
2028
2029 //qlock(&stralloc);
2030 for(l = hashslot(val); (s = *l) != nil; l = &s->next){
2031 if(strcmp(s->s, val) == 0){
2032 s->ref++;
2033 //qunlock(&stralloc);
2034 return s;
2035 }
2036 }
2037 s = malloc(sizeof(*s));
2038 s->s = strdup(val);
2039 s->len = strlen(s->s);
2040 s->ref = 1;
2041 s->next = *l;
2042 *l = s;
2043 //qunlock(&stralloc);
2044 return s;
2045 }
2046
2047 String*
dupstr(String * s)2048 dupstr(String *s)
2049 {
2050 s->ref++;
2051 return s;
2052 }
2053
2054 void
freestr(String * s)2055 freestr(String *s)
2056 {
2057 String **l;
2058
2059 if(s != nil && --s->ref == 0){
2060 //qlock(&stralloc);
2061 for(l = hashslot(s->s); *l != nil; l = &(*l)->next){
2062 if(*l == s){
2063 *l = s->next;
2064 break;
2065 }
2066 }
2067 //qunlock(&stralloc);
2068 free(s->s);
2069 free(s);
2070 }
2071 }
2072